package com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.sap;

import com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.common.WarehousePilotService;
import com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.f2b.F2bOrderHandler;
import com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.otw.common.OtwConstant;
import com.alibaba.cirtus.ots.application.adapter.ability.model.response.ScItemFromDBResponse;
import com.alibaba.cirtus.ots.application.adapter.ability.model.response.ScItemResult;
import com.alibaba.cirtus.ots.application.adapter.ability.model.response.ScItemSearchResult;
import com.alibaba.cirtus.ots.application.adapter.ability.utils.HttpTokenUtil;
import com.alibaba.cirtus.ots.application.adapter.ability.utils.HttpUtils;
import com.alibaba.cirtus.ots.application.adapter.ability.utils.InfoUtil;
import com.alibaba.citrus.cr.common.enums.ordercenter.AsyncMessageEventEnum;
import com.alibaba.citrus.cr.common.enums.ordercenter.SalesOrderPushStatusEnum;
import com.alibaba.citrus.ots.common.annotation.ProcessInvoker;
import com.alibaba.citrus.ots.common.exception.FacadeException;
import com.alibaba.citrus.ots.common.lock.RedisLock;
import com.alibaba.citrus.ots.common.util.IntegrateUtils;
import com.alibaba.citrus.ots.common.util.JsonUtil;
import com.alibaba.citrus.ots.common.util.QuantityFormatUtils;
import com.alibaba.citrus.ots.common.util.SapAndCrmFieldDealUtils;
import com.alibaba.cz.base.tool.DateFormatUtil;
import com.alibaba.cz.base.tool.Nullable;
import com.alibaba.cz.base.tool.feature.Features;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.cz.base.tool.message.ErrorCodeException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lattice2.epoch.middleware.mq.runtime.EpochMessageService;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.epoch.app.bcorder.model.dto.WarehousingOrderOperationResponse;
import com.epoch.app.bcorder.model.dto.*;
import com.epoch.app.bcorder.service.BaseDataService;
import com.epoch.app.bcorder.service.BcOrderService;
import com.epoch.app.bcorder.service.UnifiedInventoryService;
import com.epoch.app.bcots.contants.BcOtsConstants;
import com.epoch.app.crb2btradecenter.api.orderquery.service.OrderQueryService;
import com.epoch.app.crb2btradecenter.api.orderwrite.service.OrderWriteService;
import com.epoch.app.crb2btradecenter.api.sandoutgoods.service.SandOutGoodsService;
import com.epoch.app.crb2btradecenter.domain.order.dto.LoadOrderListRequest;
import com.epoch.app.crb2btradecenter.domain.order.model.Order;
import com.epoch.app.crb2btradecenter.domain.order.service.OrderService;
import com.epoch.app.crb2btradecenter.domain.orderline.dto.OrderLineLoadListRequest;
import com.epoch.app.crb2btradecenter.domain.orderline.model.OrderLine;
import com.epoch.app.crb2btradecenter.domain.orderline.service.OrderLineService;
import com.epoch.app.crb2btradecenter.dto.Packagedetails;
import com.epoch.app.crb2btradecenter.dto.request.GenerateOrderRequest;
import com.epoch.app.crb2btradecenter.dto.request.OrderPageQueryRequest;
import com.epoch.app.crb2btradecenter.dto.request.ScItemDetail;
import com.epoch.app.crb2btradecenter.dto.request.SendOutGoodsRequest;
import com.epoch.app.crb2btradecenter.dto.response.DeliveryOrderModifyResponse;
import com.epoch.app.crb2btradecenter.dto.response.OrderPageQueryResponse;
import com.epoch.app.crunifiedinventory.api.allocateorderwrite.service.AllocateOrderWriteService;
import com.epoch.app.crunifiedinventory.api.forecastwarehouse.dto.QueryWarehouseByFactoryCodeAndWarehouseCodeRequest;
import com.epoch.app.crunifiedinventory.api.forecastwarehouse.service.ForecastWarehouseService;
import com.epoch.app.crunifiedinventory.domain.allocateorder.dto.AllocateOrderLoadListRequest;
import com.epoch.app.crunifiedinventory.domain.allocateorder.model.AllocateOrder;
import com.epoch.app.crunifiedinventory.domain.allocateorder.service.AllocateOrderService;
import com.epoch.app.crunifiedinventory.domain.allocateorderdetail.dto.AllocateOrderDetailLoadListRequest;
import com.epoch.app.crunifiedinventory.domain.allocateorderdetail.model.AllocateOrderDetail;
import com.epoch.app.crunifiedinventory.domain.allocateorderdetail.service.AllocateOrderDetailService;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderReceivedRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderReceivedScItemDTO;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderShippedRequest;
import com.epoch.app.crunifiedinventory.dto.request.AllocateOrderShippedScItemDTO;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderDetailsQueryResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderReceivedResponse;
import com.epoch.app.crunifiedinventory.dto.response.AllocateOrderShippedResponse;
import com.epoch.app.crunifiedinventory.materialretry.dto.GetFailRecordBySAPMertrailIdRequest;
import com.epoch.app.crunifiedinventory.materialretry.model.MaterialRetry;
import com.epoch.app.crunifiedinventory.materialretry.service.MaterialRetryService;
import com.epoch.app.crunifiedinventory.model.dto.*;
import com.epoch.app.crunifiedinventory.model.enums.FulfillmentTypeEnum;
import com.epoch.app.crunifiedinventory.service.CrUnifiedInventoryService;
import com.epoch.app.mninventorycenter.model.dto.BusinessPlanOrderUpdateRequest;
import com.epoch.app.mninventorycenter.service.MnInventoryCenterService;
import com.epoch.app.mnordercenter.model.dto.CommonAsyncMessageResponse;
import com.epoch.app.mnordercenter.model.dto.PushOrderInfoToMnSideRequestOrderInfo;
import com.epoch.app.otsapplicationadaptersdk.contants.OtsApplicationAdapterSdkConstants;
import com.epoch.app.otsapplicationadaptersdk.materialsyncretry.model.MaterialSyncRetry;
import com.epoch.app.otsapplicationadaptersdk.materialsyncretry.service.MaterialSyncRetryService;
import com.epoch.app.otsapplicationadaptersdk.mnotwif011.service.MnOtwIf011Service;
import com.epoch.app.otsapplicationadaptersdk.model.dto.*;
import com.epoch.app.otsapplicationadaptersdk.model.dto.CheckPilotByLogicWarehouseCodeRequest;
import com.epoch.app.otsapplicationadaptersdk.sap.*;
import com.epoch.app.otsapplicationadaptersdk.sapv2.mnsapif004v2.service.MnSapIf004V2Service;
import com.epoch.app.otsapplicationadaptersdk.service.OtsApplicationAdapterSdkService;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author hanguo
 * @Description sap请求平台同步物料凭证
 * @date 2021/9/25
 */
@Component
@Primary
public class MnSapIf004V2ServiceImpl implements MnSapIf004V2Service {
    private final Log log = Log.getLogger(MnSapIf004V2ServiceImpl.class);

    @Autowired
    private AllocateOrderWriteService allocateOrderWriteService;
    @Autowired
    private SandOutGoodsService sandOutGoodsService;
    @Autowired
    private CrUnifiedInventoryService crUnifiedInventoryService;
    @Resource
    private BaseDataService baseDataService;
    @Resource
    private BcOrderService bcOtsService;
    @Resource
    private OtsApplicationAdapterSdkService otsApplicationAdapterSdkService;
    @Resource
    private OrderWriteService orderWriteService;
    @Resource
    private ForecastWarehouseService forecastWarehouseService;
    @Resource
    private MaterialRetryService retryService;
    @Resource
    private AllocateOrderService allocateOrderService;
    @Resource
    private OrderQueryService orderQueryService;
    @Resource
    private MaterialSyncRetryService materialSyncRetryService;
    @Resource
    private UnifiedInventoryService unifiedInventoryService;

    @Autowired
    private List<F2bOrderHandler> f2bOrderHandlers;

    @Resource
    private EpochMessageService messageService;
    @Resource
    private MnOtwIf011Service mnOtwIf011Service;
    @Resource
    OrderService orderService;

    @Resource
    private MnInventoryCenterService mnInventoryCenterService;
    @Resource
    private AllocateOrderDetailService allocateOrderDetailService;
    @Resource
    private OrderLineService orderLineService;

    @Resource
    private WarehousePilotService warehousePilotService;

    private static final List<String> excludeSapTypes = Lists.newArrayList("601", "602");
    //销售单元仓类型为3
    private static final String salesWarehouseType = "3";
    //牛要客销售组织
    private static final String NYK_ORG_ID = "298622740826374971";
    public static final String MATERIAL_LOCK = "material_lock_";
    //业务单据号
    private static final String  BIZ_ORDER_ID = "bizOrderId";
    //业务单据行号
    private static final String  BIZ_SUB_ORDER_ID = "bizSubOrderId";

    @Autowired
    private HttpUtils httpUtils;
    //履约方式编码
    private static final String FULFILLMENT_TYPE_CODE = "fulfillmentTypeCode";
    private static final Integer MAX_PAGE_SIZE = 1000;
    private static final String MATERIAL_TYPE = "[{\"codeType\":\"601\",\"codeTypeDesc\":\"GD发货: 交货\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"602\",\"codeTypeDesc\":\"RE交货冲销\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"Y61\",\"codeTypeDesc\":\"\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"Y62\",\"codeTypeDesc\":\"\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"301\",\"codeTypeDesc\":\"工厂间的库存调拨转移\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"301\",\"codeTypeDesc\":\"工厂间的库存调拨转移\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"302\",\"codeTypeDesc\":\"TR 工厂间的转储\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"302\",\"codeTypeDesc\":\"TR 工厂间的转储\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"303\",\"codeTypeDesc\":\"TF 部件从库存到工厂\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"303\",\"codeTypeDesc\":\"TF 部件从库存到工厂\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"304\",\"codeTypeDesc\":\"TR 部件从库存到工厂\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"304\",\"codeTypeDesc\":\"TR 部件从库存到工厂\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"305\",\"codeTypeDesc\":\"TF 厂内库存计划\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"305\",\"codeTypeDesc\":\"TF 厂内库存计划\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"306\",\"codeTypeDesc\":\"TR 厂内库存计划\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"306\",\"codeTypeDesc\":\"TR 厂内库存计划\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"311\",\"codeTypeDesc\":\"TF 厂内移储\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"311\",\"codeTypeDesc\":\"TF 厂内移储\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"312\",\"codeTypeDesc\":\"TR 厂内转储\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"312\",\"codeTypeDesc\":\"TR 厂内转储\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"313\",\"codeTypeDesc\":\"从库到存储地点的调拨\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"313\",\"codeTypeDesc\":\"从库到存储地点的调拨\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"314\",\"codeTypeDesc\":\"TR 部件从库到库存地\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"314\",\"codeTypeDesc\":\"TR 部件从库到库存地\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"315\",\"codeTypeDesc\":\"TF 库存地库存计划\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"315\",\"codeTypeDesc\":\"TF 库存地库存计划\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"316\",\"codeTypeDesc\":\"TR 库存地库存计划\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"316\",\"codeTypeDesc\":\"TR 库存地库存计划\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"309\",\"codeTypeDesc\":\"TF转储采购物料到物料\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"309\",\"codeTypeDesc\":\"TF转储采购物料到物料\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"310\",\"codeTypeDesc\":\"TR转储采购物料到物料\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"310\",\"codeTypeDesc\":\"TR转储采购物料到物料\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"343\",\"codeTypeDesc\":\"TF 冻结到非限制\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"343\",\"codeTypeDesc\":\"TF 冻结到非限制\",\"mainOrderType\":2,\"inventoryType\":2,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"344\",\"codeTypeDesc\":\"TR 冻结到非限制\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"344\",\"codeTypeDesc\":\"TR 冻结到非限制\",\"mainOrderType\":1,\"inventoryType\":2,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"321\",\"codeTypeDesc\":\"TF质量到非限制\",\"mainOrderType\":2,\"inventoryType\":3,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"321\",\"codeTypeDesc\":\"TF质量到非限制\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"322\",\"codeTypeDesc\":\"TR 质量不受限制\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"322\",\"codeTypeDesc\":\"TR 质量不受限制\",\"mainOrderType\":1,\"inventoryType\":3,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"551\",\"codeTypeDesc\":\"发货报废\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"552\",\"codeTypeDesc\":\"RE报废\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"201\",\"codeTypeDesc\":\"有关成本中心的发货\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"202\",\"codeTypeDesc\":\"有关成本中心的收货\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"101\",\"codeTypeDesc\":\"GR 收货\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"102\",\"codeTypeDesc\":\"采购订单冲销的收货\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"161\",\"codeTypeDesc\":\"收货退货\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"162\",\"codeTypeDesc\":\"收货退给冲销\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"641\",\"codeTypeDesc\":\"TF 到在途库存\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"641\",\"codeTypeDesc\":\"TF 到在途库存\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"642\",\"codeTypeDesc\":\"TR 在途库存\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"642\",\"codeTypeDesc\":\"TR 在途库存\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"643\",\"codeTypeDesc\":\"TF 至跨公司间\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"643\",\"codeTypeDesc\":\"TF 至跨公司间\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"644\",\"codeTypeDesc\":\"TR 至跨公司间\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"644\",\"codeTypeDesc\":\"TR 至跨公司间\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"671\",\"codeTypeDesc\":\"TR 在途库存\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"671\",\"codeTypeDesc\":\"TR 在途库存\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"672\",\"codeTypeDesc\":\"TF 到在途库存\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"672\",\"codeTypeDesc\":\"TF 到在途库存\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"673\",\"codeTypeDesc\":\"TR 至跨公司间\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"673\",\"codeTypeDesc\":\"TR 至跨公司间\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"674\",\"codeTypeDesc\":\"TF 至跨公司间\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"674\",\"codeTypeDesc\":\"TF 至跨公司间\",\"mainOrderType\":3,\"inventoryType\":1,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"657\",\"codeTypeDesc\":\"GD 退货冻结\",\"mainOrderType\":1,\"inventoryType\":2,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"658\",\"codeTypeDesc\":\"GD 退货冻结冲销\",\"mainOrderType\":2,\"inventoryType\":2,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"ZF1\",\"codeTypeDesc\":\"\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"ZF2\",\"codeTypeDesc\":\"\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"ZF3\",\"codeTypeDesc\":\"\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"ZF4\",\"codeTypeDesc\":\"\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"ZF5\",\"codeTypeDesc\":\"\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":0},{\"codeType\":\"ZF6\",\"codeTypeDesc\":\"\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":1},{\"codeType\":\"ZF7\",\"codeTypeDesc\":\"\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"ZF8\",\"codeTypeDesc\":\"\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"701\",\"codeTypeDesc\":\"GR实际盘点: 仓库\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"702\",\"codeTypeDesc\":\"GI实际盘点: 仓库\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":1},{\"codeType\":\"561\",\"codeTypeDesc\":\"初始库存条目\",\"mainOrderType\":1,\"inventoryType\":0,\"outInType\":\"S\",\"reverse\":0},{\"codeType\":\"562\",\"codeTypeDesc\":\"RE初始条目库存余额\",\"mainOrderType\":2,\"inventoryType\":0,\"outInType\":\"H\",\"reverse\":1}]";
    private static final Integer OUT = 2;


    /**
     * 物料凭证都一条条给。 为了兼容其他接口。request里面是list
     *
     * @param mnCrmIf004Request
     * @return
     */
    @Override
    @ProcessInvoker(remark = "MnSapIf004V2Service", errorCode = "OTS-02-007-00-16-999", printLog = true)
    public MnSapCommResponse externalSyncMaterial(MnSapIf004Request mnCrmIf004Request) {
        log.warn("MnSapIf004ServiceImpl.externalSyncMaterial,param:{}", mnCrmIf004Request == null ? "" : JSON.toJSONString(mnCrmIf004Request));
        CreatOutinOrderRequest outin = new CreatOutinOrderRequest();
        List<MnSapIf004SDO> dataList = mnCrmIf004Request.getData();
        if (CollectionUtils.isEmpty(dataList)) {
            String dataJson = IntegrateUtils.safeGetDataJson();
            if (StringUtils.isEmpty(dataJson)) {
                throw new FacadeException("OTS-03-001-01-15-004");
            }
            dataList = JSON.parseArray(dataJson, MnSapIf004SDO.class);
            if (CollectionUtils.isEmpty(dataList)) {
                throw new FacadeException("OTS-03-001-01-15-004");
            }
        }
        MnSapIf004SDO sapMaterial = dataList.get(0);
        String sendTime = Optional.ofNullable(mnCrmIf004Request).map(MnSapIf004Request::getMessageHeader).map(MessageHeaderSDO::getSendTime).orElse(null);


        /**
         *         {
         * [{
         *       "HeaderKey": "HeaderKey 原样返回",
         *       "STATUS": "S/E，S成功，E失败",
         *       "ERRCODE": "错误代码",
         *       "MESSAGE": "消息描述",
         *       "EXT_ID": "订单平台内部业务ID",
         *       "Item": [
         *           "Item_key": "行项目ID，有行项目则原样返回",
         *           "STATUS": "S/E，S成功，E失败",
         *           "MESSAGE": "消息描述",
         *         }
         *       ]
         *     }] 组装返回参数
         */
        MnSapCommResponse mnSapCommResponse = new MnSapCommResponse();
        MessageHeaderSDO messageHeader = mnCrmIf004Request.getMessageHeader();
        if (Objects.isNull(messageHeader)) {
            messageHeader = IntegrateUtils.safeGetMessageHeader(MessageHeaderSDO.class);
        }
        if (Objects.nonNull(messageHeader)) {
            mnSapCommResponse.setInterfacePath(messageHeader.getInterfacePath());
            mnSapCommResponse.setMessageId(messageHeader.getMessageId());
        }

        //凭证的返回list
        List<MnSapCommSDO> responseList = new ArrayList<>();
        //凭证下批次的返回list
        List<MnSapCommDetail> itemError = new ArrayList<>();
        MnSapCommSDO mnSapCommSDO = new MnSapCommSDO();
        mnSapCommSDO.setHeaderKey(sapMaterial.getHeaderKey());
        mnSapCommSDO.setStatus("S");
        String outMaterialCertificateId = sapMaterial.getOutMaterialCertificateId();
        String lockKey = MATERIAL_LOCK + outMaterialCertificateId;
        try (RedisLock saleLock = new RedisLock(BcOtsConstants.REDIS_CACHE_CODE(), lockKey,
                2 * 60 * 1000, 10 * 60 * 1000)) {
            if (!saleLock.lock()) {
                List<MnSapCommSDO> mnSapCommSDOS = new ArrayList<>();
                mnSapCommSDO.setMessage("获取物料凭证分布式锁错误");
                mnSapCommSDO.setStatus("E");
                mnSapCommSDOS.add(mnSapCommSDO);
                mnSapCommResponse.setData(mnSapCommSDOS);
                //写入重试表
                saveRetryLogs(sapMaterial.getOutMaterialCertificateId(), JSON.toJSONString(mnCrmIf004Request), "获取物料凭证分布式锁错误");
                log.error("物料凭证Exception:" + sapMaterial.getOutMaterialCertificateId() + "报错,errorMsg:获取物料凭证分布式锁错误");
                return mnSapCommResponse;
            }
            boolean inPilotScope = isInPilotScope(sapMaterial);
            log.info("MnSapIf004V2$externalSyncMaterial inPilotScope={}", inPilotScope);
            outin = packageOutInOrderRequest(outin, sapMaterial, itemError);
            if (inPilotScope){
                log.info("004 sap物料凭证同步中台，走一盘货逻辑");
                //走试点逻辑
                // 1、update出入库单 记录物料凭证信息,2同步到OTW
                updateWarehousingOrder(mnCrmIf004Request, sapMaterial);

                //通知调拨 销售单 改状态 通知创建 销售单
               if (outin != null){
                   afterInventory(outin, sapMaterial.getOutMaterialCertificateId(), sendTime,true);
               }
                //处理在途库存 TODO 2024.04.09 004更新物料凭证 不需要操作在途
//                changeTransitInventoryQuantity(dataList,outin);

                asyncSendMsgToKafka(sapMaterial.getOutSourceOrderId());

                //计划单修改产量
                updatePlanOrder(dataList);

                //发消息给F2B
                sendMessageToF2B(sapMaterial);

                responseList.add(mnSapCommSDO);
                mnSapCommResponse.setData(responseList);
                return mnSapCommResponse;
            }

            //是否是冲销的 冲销的
            if (outin != null) {
                String isAllocate = outin.getFeatures().getFeatureMap().get("isAllocate");
                if (Objects.equals("1", isAllocate)) {
                    //1、如果是调拨单，仓库在试点范围的会转到bclink处理。仓库不在试点范围内的，需要判断调拨单的履约方式，只要履约方式包含试点仓就 打上 os=“BC”
                    //2、如果是销售订单，无需处理
                    String fulfillmentTypeCode = outin.getFeatures().getFeatureMap().get("fulfillmentTypeCode");
                    List<String> includePilotList = Lists.newArrayList(
                        FulfillmentTypeEnum.PILOT2PILOT.getValue().toString(),
                        FulfillmentTypeEnum.PILOT2CURRENT.getValue().toString(),
                        FulfillmentTypeEnum.CURRENT2PILOT.getValue().toString(),
                        FulfillmentTypeEnum.VIRTUAL_TRANSFER.getValue().toString());
                    if (includePilotList.contains(fulfillmentTypeCode)) {
                        outin.getFeatures().getFeatureMap().put("os", "BC");
                    }
                }

                String isReverseStr = outin.getFeatures().getFeatureMap().get("isReverse");
                Boolean isReverse = StringUtils.isBlank(isReverseStr) ? false : Boolean.valueOf(isReverseStr);
                String hasInorderStr = outin.getFeatures().getFeatureMap().get("hasInOrder");
                Boolean hasInorder = StringUtils.isBlank(hasInorderStr) ? false : Boolean.valueOf(hasInorderStr);
                if (isReverse) {
                    /**
                     * 2021.11.23 新增冲销功能。
                     * 1.出入库单照旧记录
                     * 2.根据交货单号，找到原调拨单和出入库单
                     * 3.原出入库单状态 改为已冲销
                     * 4.原出入库单的库存变更还原
                     * 5.只有出的类型明细会进行冲销，入的报错出去
                     * 2021.12.13 新增入库冲销
                     *  1.入库的冲销不通知销售单，只变更库存
                     *  2.入库类型的冲销，通知调拨单
                     *  2021.12.16
                     *    1.销售退货 冲销入库
                     *    2.销售换货  不通知销售单冲销
                     *
                     */
                    reverseOutinOrder(outin, sapMaterial, hasInorder, mnCrmIf004Request);
                } else {
                    log.info("MnSapIf004ServiceImpl.externalSyncMaterial bcOtsService.createOutinOrders,request:{}", JSON.toJSONString(outin));
                    Result<WarehousingOrderOperationResponse> result = bcOtsService.createOutinOrders(outin);
                    log.info("MnSapIf004ServiceImpl.externalSyncMaterial bcOtsService.createOutinOrders,result{}", result == null ? "" : JSON.toJSONString(result));
                    if (result.isSuccess()) {
                        //通知调拨 销售单 改状态 通知创建 销售单
                        afterInventory(outin, sapMaterial.getOutMaterialCertificateId(), sendTime,false);
                    }
                    if (!result.isSuccess()) {
                        saveRetryLogs(sapMaterial.getOutMaterialCertificateId(), JSON.toJSONString(mnCrmIf004Request),
                                StringUtils.isNotBlank(result.getMessage()) ? result.getMessage() :
                                        "库存操作失败");
                        throw new FacadeException("OTS-03-002-01-15-012", "请前往系统日志搜索并重试接口");
                    }
                    //如果调用成功了 有外部单号 但是找不到调拨单/销售单 就放入重试 2022.05.27
                    // 2022.06.07 排除移动类型为601 or 602，根据工厂+库存地点关联我们的逻辑仓库为销售单元仓，同时销售单元仓对应的销售组织为6140
                    //2022.06.14 需要重试物料过多,其中有些单据sap一直不同步过来, 重试有问题, 暂时先去除限制了
                    if (result.isSuccess()) {
                        if (isBeforeSourceOrder(outin, sapMaterial)) {
                            saveMaterialRetry(mnCrmIf004Request, sapMaterial);
                        } else {
                            updateMaterialRetry(mnCrmIf004Request, sapMaterial);
                        }
//                        throw new FacadeException("OTS-03-002-01-15-112");

                        saveShippingOrder(result.getResult().getWarehousingOrderId(), mnCrmIf004Request);
                    }
                }
            }
            mnSapCommSDO.setItem(itemError);
            responseList.add(mnSapCommSDO);
            mnSapCommResponse.setData(responseList);

            //处理在途库存
            changeTransitInventoryQuantity(dataList,outin);

            asyncSendMsgToKafka(sapMaterial.getOutSourceOrderId());

            //计划单修改产量
            updatePlanOrder(dataList);

            //发消息给F2B
            this.sendMessageToF2B(sapMaterial);

        } catch (ErrorCodeException e) {
            //如果是货品找不到 放入重试
//            if (e != null &&
//                    ("OTS-03-001-01-15-002".equals(e.getErrorCode()) || "OTS-03-001-01-15-015".equals(e.getErrorCode())
//                    || "OTS-03-002-01-15-112".equals(e.getErrorCode()))){
//            }
            //2022.05.20 把这些异常都加入重试表
            //2022.10.08 写入错误信息
            saveRetryLogs(sapMaterial.getOutMaterialCertificateId(), JSON.toJSONString(mnCrmIf004Request), e.getMessage());
            mnSapCommSDO.setStatus("E");
            log.error("物料凭证ErrorCodeException:" + sapMaterial.getOutMaterialCertificateId() + "报错", e);
            throw e;
        } catch (Throwable e) {
            mnSapCommSDO.setStatus("E");
            saveRetryLogs(sapMaterial.getOutMaterialCertificateId(), JSON.toJSONString(mnCrmIf004Request), e.getMessage());
            log.error("物料凭证Exception:" + sapMaterial.getOutMaterialCertificateId() + "报错,errorMsg:" + e.getMessage(), e);
            throw new FacadeException("OTS-03-002-01-15-010");
        } finally {
            log.warn("MnSapIf004ServiceImpl.externalSyncMaterial,response:{}", JSON.toJSONString(mnSapCommResponse));
        }
        return mnSapCommResponse;
    }

    //private String getOSFlag(MnSapIf004SDO sapMaterial){
    //    AllocateOrderLoadListRequest allocateOrderLoadListRequest = new AllocateOrderLoadListRequest();
    //    allocateOrderLoadListRequest.setOutAllocateOrderId(sapMaterial.getOutAllocateOrderId());
    //    log.info("017 loadAllocateOrderList req={}", JSON.toJSONString(allocateOrderLoadListRequest));
    //    Result<List<AllocateOrder>> loadAllocateOrderList = allocateOrderService.loadAllocateOrderList(allocateOrderLoadListRequest);
    //    log.info("017 loadAllocateOrderList res={}", JSON.toJSONString(loadAllocateOrderList));
    //    if (loadAllocateOrderList == null || CollectionUtils.isEmpty(loadAllocateOrderList.getResult())) {
    //        // 查不到调拨单，再查销售单
    //        LoadOrderListRequest loadOrderListRequest = new LoadOrderListRequest();
    //        loadOrderListRequest.setOuterOrderId(sapMaterial.getOutSourceOrderId());
    //        log.info("017 loadOrderList c req={}", JSON.toJSONString(loadOrderListRequest));
    //        Result<List<Order>> listResult = orderService.loadOrderList(loadOrderListRequest);
    //        log.info("017 loadOrderList c res={}", JSON.toJSONString(listResult));
    //        if (Objects.isNull(listResult) || CollectionUtils.isEmpty(listResult.getResult())) {
    //            // 不去掉0再查一次
    //            loadOrderListRequest.setOuterOrderId(sapMaterial.getOutSourceOrderId());
    //            log.info("017 loadOrderList d req={}", JSON.toJSONString(loadOrderListRequest));
    //            listResult = orderService.loadOrderList(loadOrderListRequest);
    //            log.info("017 loadOrderList d res={}", JSON.toJSONString(listResult));
    //            if (Objects.isNull(listResult) || CollectionUtils.isEmpty(listResult.getResult())) {
    //
    //            }
    //        }
    //    }
    //    return null;
    //}

    //更新出入库单
    private void updateWarehousingOrder(MnSapIf004Request mnCrmIf004Request, MnSapIf004SDO sapMaterial) {
        // 处理逻辑放在了bc-link处理：MnSAP004Handler
        toBcLink(mnCrmIf004Request, sapMaterial);
        /*if (StringUtils.isBlank(sapMaterial.getOutDeliveryOrderId())) {
            log.info("004 updateWarehousingOrder skip outDeliveryOrderId is null");
            return;
        }
        QueryWmsOrderByOtherOrderRequest queryWmsOrderByOtherOrderRequest = new QueryWmsOrderByOtherOrderRequest();
        queryWmsOrderByOtherOrderRequest.setPrevWmsOrderId(InfoUtil.deleteZeroPrefix(sapMaterial.getOutDeliveryOrderId()));
        queryWmsOrderByOtherOrderRequest.setContainSubOrders(true);
        log.info("004queryInOutBoundByDelivery request:{}", JSON.toJSONString(queryWmsOrderByOtherOrderRequest));
        Result<WarehousingOrderDTOResponse> result = otsApplicationAdapterSdkService.queryWmsOrderByOtherOrder(
            queryWmsOrderByOtherOrderRequest);
        log.info("004queryInOutBoundByDelivery response:{}", JSON.toJSONString(result));
        if (result.isSuccess() && null != result.getResult() && CollectionUtils.isNotEmpty(
            result.getResult().getWarehousingOrders())) {
            boolean inBound = "101".equals(sapMaterial.getItems().get(0).getSapType());
            WarehousingOrderDTO warehousingOrderDTO = result.getResult().getWarehousingOrders().stream().filter(r -> {
                boolean inType;
                if (r.getMainOrderType() != null) {
                    inType = r.getMainOrderType().equals(WmsOrderMainTypeEnum.IN.getValue())
                        || r.getMainOrderType().equals(WmsOrderMainTypeEnum.RETURN_IN.getValue())
                        || r.getMainOrderType().equals(WmsOrderMainTypeEnum.ALLOCATE_IN.getValue())
                        || r.getMainOrderType().equals(WmsOrderMainTypeEnum.OTHER_IN.getValue());
                } else {
                    inType = WmsOrderMainTypeEnum.IN.getValue().equals(r.getWmsSubOrders().get(0).getMainOrderType());
                }
                return inType == inBound;
            }).findFirst().orElseThrow(
                () -> new FacadeException("OTS-03-002-01-15-012", "未找到对应的" + (inBound ? "入" : "出") + "库单"));
            WarehousingOrderDTO updateWarehousingOrder = new WarehousingOrderDTO();
            updateWarehousingOrder.setWarehousingOrderId(warehousingOrderDTO.getWarehousingOrderId());
            updateWarehousingOrder.setFromSys(warehousingOrderDTO.getFromSys());
            updateWarehousingOrder.setBusinessType(warehousingOrderDTO.getBusinessType());
            updateWarehousingOrder.setOrderType(warehousingOrderDTO.getOrderType());
            updateWarehousingOrder.setMainOrderType(warehousingOrderDTO.getMainOrderType());
            updateWarehousingOrder.setOrderStatus(warehousingOrderDTO.getOrderStatus());
            updateWarehousingOrder.setOutWarehousingOrderId(sapMaterial.getOutMaterialCertificateId());
            //updateWarehousingOrder.setPrevWmsOrderId(sapMaterial.getOutDeliveryOrderId());
            //updateWarehousingOrder.setDeliveryOrderId(sapMaterial.getOutDeliveryOrderId());
            Map<String, String> featuresMap = new HashMap<>();
            featuresMap.put("materialCertificationId", sapMaterial.getOutMaterialCertificateId());
            updateWarehousingOrder.setFeatureMap(featuresMap);
            log.info("004updateWmsOrderInner request:{}", JSON.toJSONString(updateWarehousingOrder));
            Result<Void> updateResult = otsApplicationAdapterSdkService.updateWmsOrderInner(updateWarehousingOrder);
            log.info("004updateWmsOrderInner result:{}", JSON.toJSONString(updateResult));
            if (!updateResult.isSuccess()) {
                saveRetryLogs(sapMaterial.getOutMaterialCertificateId(), JSON.toJSONString(mnCrmIf004Request),
                    StringUtils.isNotBlank(result.getMessage()) ? result.getMessage() : "更新出入库单失败");
                throw new FacadeException("OTS-03-002-01-15-012", "更新出入库单失败");
            }
            //调用011接口
            MnOtwIf011Request mnOtwIf011Request = new MnOtwIf011Request();
            //中台出入库单号
            mnOtwIf011Request.setOutboundOrderNo(warehousingOrderDTO.getWarehousingOrderId());
            //订单类型 in/out
            String type =  inBound ? "IN" : "OUT";
            mnOtwIf011Request.setTransType(type);
            //mws发运单号
            mnOtwIf011Request.setOuterOutboundOrderNo(warehousingOrderDTO.getSourceWarehousingOrderId());
            //是否过账成功
            mnOtwIf011Request.setSuccess("Y");
            log.info("004InOutBoundPostResult011 request:{}", JSON.toJSONString(mnOtwIf011Request));
            MnOtwCommonResponse otwCommonResponse  = mnOtwIf011Service.outInWarehousePostResultSync(mnOtwIf011Request);
            log.info("004InOutBoundPostResult011 response:{}", JSON.toJSONString(otwCommonResponse));
            if (Boolean.FALSE.toString().equals(otwCommonResponse.getSuccess())) {
                log.error("InOutBoundPostResult011 失败 otwCommonResponse:{}",JSONObject.toJSONString(otwCommonResponse));
            }
            // todo wxy 判断场景，更新装运单上面的交货单id
            //shippingOrderDetailUpdateInOutBoundOrder(warehousingOrderDTO.getWarehousingOrderId(), sapMaterial.getOutDeliveryOrderId());
        } else {
            saveRetryLogs(sapMaterial.getOutMaterialCertificateId(), JSON.toJSONString(mnCrmIf004Request),
                StringUtils.isNotBlank(result.getMessage()) ? result.getMessage() : "出入库单不存在");
            throw new FacadeException("OTS-03-002-01-15-012", "出入库单不存在");
        }*/
    }

    private boolean toBcLink(MnSapIf004Request mnCrmIf004Request,MnSapIf004SDO sapMaterial) {
        log.info("MnSapIf004V2ServiceImpl boBcLink param:{}", JSON.toJSONString(mnCrmIf004Request));
        try {
            Map<String, String> header = HttpTokenUtil.generateHttpHeader();
            String urlPrefix = OtsApplicationAdapterSdkConstants.urlPrefix();
            String url = urlPrefix + "api/mn_bc_link/sap004Detail";
            JSONObject message = JSON.parseObject(JSON.toJSONString(mnCrmIf004Request));
            message.put("key", mnCrmIf004Request.getData().get(0).getOutMaterialCertificateId());
            JSONObject jsonObject=new JSONObject();
            jsonObject.put("message", message.toJSONString());
            log.info("MnSapIf004V2ServiceImpl boBcLinkcall bc link interface,url:{}, header:{}, " + "param:{}",
                url, header, jsonObject.toJSONString());
            ResponseEntity<com.alibaba.cirtus.ots.application.adapter.ability.model.Result> post = httpUtils.post(url,
                header, jsonObject, com.alibaba.cirtus.ots.application.adapter.ability.model.Result.class);
            com.alibaba.cirtus.ots.application.adapter.ability.model.Result result = post.getBody();
            log.info("MnSapIf004V2ServiceImpl boBcLink result:{}", JSON.toJSONString(result));
            return result.isSuccess();
        } catch (Exception e) {
            log.error("MnSapIf004V2ServiceImpl boBcLink error", e);
            saveRetryLogs(sapMaterial.getOutMaterialCertificateId(), JSON.toJSONString(mnCrmIf004Request),
            "call bcLink sap004Detail error");
            throw new FacadeException("OTS-03-002-01-15-012", "调用bc-link接口失败");
        }
    }

    /**
     * 发送mq消息，异步发送kafka消息通知应用端
     */
    private void asyncSendMsgToKafka(String outSourceOrderId) {
        if (StringUtils.isBlank(outSourceOrderId)) {
            return;
        }

        LoadOrderListRequest pageQueryRequest = new LoadOrderListRequest();
        pageQueryRequest.setOuterOrderId(outSourceOrderId);
        Result<List<Order>> listResult = orderService.loadOrderList(pageQueryRequest);
        if (Objects.isNull(listResult) || CollectionUtils.isEmpty(listResult.getResult())) {
            return;
        }
        Order order = listResult.getResult().get(0);
        if (!JsonUtil.checkOrderCenterTag(order.getFeatures())) {
            // 非订单中心订单不发kafka消息
            return;
        }

        PushOrderInfoToMnSideRequestOrderInfo pushOrderInfo = new PushOrderInfoToMnSideRequestOrderInfo();
        pushOrderInfo.setSalesOrderNo(order.getOrderBizId());
        pushOrderInfo.setOrderStatus(SalesOrderPushStatusEnum.SUCCESS_UPDATE.getOrderStatusCode());

        CommonAsyncMessageResponse commonAsyncMessageResponse = new CommonAsyncMessageResponse();
        commonAsyncMessageResponse.setEventCode(AsyncMessageEventEnum.SALES_ORDER_UPDATE.getValue());
        commonAsyncMessageResponse.setEventName(AsyncMessageEventEnum.SALES_ORDER_UPDATE.getLabel());
        commonAsyncMessageResponse.setEventTime(String.valueOf(new Date().getTime()));
        commonAsyncMessageResponse.setFromSys(order.getFromSys());
        commonAsyncMessageResponse.setBusinessUnitCode(order.getBisUnitOutCode());
        commonAsyncMessageResponse.setData(Arrays.asList(pushOrderInfo));
        messageService.sendMessage("ORDER_STATUSCHANGE_PUSH_MESSAGE", "CR_SALEORDER_V2", "SaleOrderPushMn",
                JSONObject.toJSONString(commonAsyncMessageResponse));
        log.info("MnSapIf004V2Service#asynSendMsgToKafka commonAsyncMessageResponse: {}", commonAsyncMessageResponse);
    }

    /**
     * 处理发消息给F2B场景
     *
     * @param mnSapIf004SDO
     */
    private void sendMessageToF2B(MnSapIf004SDO mnSapIf004SDO) {
        log.info("MnSapIf004V2ServiceImpl.sendMessageToF2B request:{}", JSONObject.toJSONString(mnSapIf004SDO));
        try {
            if (Objects.isNull(mnSapIf004SDO)) {
                return;
            }
            for (F2bOrderHandler f2bOrderHandler : f2bOrderHandlers) {
                if (f2bOrderHandler.getF2bOrderFlag(mnSapIf004SDO)) {
                    log.info("MnSapIf004V2ServiceImpl.sendMessageToF2B_handler:{}", JSONObject.toJSONString(mnSapIf004SDO));
                    f2bOrderHandler.sendMessageToF2b(mnSapIf004SDO);
                }
            }
        } catch (Exception e) {
            log.error("MnSapIf004V2ServiceImpl.sendMessageToF2B-发送F2B消息失败！原因:{}", e);
        }

    }

    /**
     * 判断004接口是否在调拨单和销售单之前, 如果是就报错重新放入重试表
     *
     * @param request
     * @param sdo
     * @return
     */
    private boolean isBeforeSourceOrder(CreatOutinOrderRequest request, MnSapIf004SDO sdo) {
        EpochFeatures features = request.getFeatures();
        Map<String, String> featureMap = features.getFeatureMap();
        if (featureMap == null) {
            return false;
        }
        String isAllocate = features.getFeatureMap().get("isAllocate");
        if ("0".equals(isAllocate) && !StringUtils.isAllBlank(sdo.getOutSourceOrderId(), sdo.getOutAllocateOrderId())) {
            //2022.06.07 排除移动类型为601 or 602，根据工厂+库存地点关联我们的逻辑仓库为销售单元仓，同时销售单元仓对应的销售组织为6140
//            if (CollectionUtils.isNotEmpty(request.getGoods())){
//                List<CreateOutinOrderGoodsRequest> goods = request.getGoods();
//                for (CreateOutinOrderGoodsRequest good : goods) {
//                    String sapType = good.getSapType();
//                    String warehouseAreaType = good.getWarehouseAreaType();
//                    if (excludeSapTypes.contains(sapType) && salesWarehouseType.equals(warehouseAreaType)
//                        && NYK_ORG_ID.equals(good.getWarehouseAreaOrgId())){
//                        return false;
//                    }
//                }
//            }
//            //判断下是否是牛要客的销售单 如果是牛要客的 就跳过 因为牛要客的订单 外部编码存在了nykid上
//            AllocateOrderLoadListRequest allocateOrderLoadListRequest = new AllocateOrderLoadListRequest();
//            if (StringUtils.isNotBlank(sdo.getOutSourceOrderId())){
//                allocateOrderLoadListRequest.setNykId(sdo.getOutSourceOrderId());
//            }
//            if (StringUtils.isNotBlank(sdo.getOutAllocateOrderId())){
//                allocateOrderLoadListRequest.setNykId(sdo.getOutAllocateOrderId());
//            }
//            Result<List<AllocateOrder>> loadAllocateOrderList = allocateOrderService.loadAllocateOrderList(allocateOrderLoadListRequest);
//            if (loadAllocateOrderList != null && CollectionUtils.isNotEmpty(loadAllocateOrderList.getResult())){
//                return false;
//            }
            return true;
        }
        return false;
    }

    /**
     * 组装物料凭证 批次请求信息
     *
     * @param subTmp
     * @param outin
     * @return
     */
    private CreateOutinOrderGoodsRequest packageItem(MnSapIf004Detail subTmp, CreatOutinOrderRequest outin, Features outinFeatures, Map<String, ScItemSDO> itemSDOMap, MnSapIf004SDO mateial,
                                                     String bizSubOrderId) {
        CreateOutinOrderGoodsRequest subOrder = new CreateOutinOrderGoodsRequest();
        subOrder.setGoodsId(itemSDOMap.get(subTmp.getScItemId()).getScItemId());
        subOrder.setBatchNo(subTmp.getBatchId());
        subOrder.setGoodsName(itemSDOMap.get(subTmp.getScItemId()).getTitle());
        subOrder.setGoodsUnit(subTmp.getBaseUnit());
        subOrder.setSourceSubOrderId(outin.getSourceWarehousingOrderId());
        subOrder.setLineNo(subTmp.getOutLineId());
        //设置销售单外部行号
        subOrder.setOutSourceOrderLineId(InfoUtil.deleteZeroPrefix(subTmp.getOutSourceOrderLineId()));
        //调拨单外部行号 去掉前缀0
        subOrder.setOutAllocateOrderLineId(InfoUtil.deleteZeroPrefix(subTmp.getOutAllocateOrderLineId()));

        try {
            //数量乘1000处理落库  乘1000的只针对基本单位数量操作。 给销售单的是销售数量不做处理
            //通知调拨销售单的数量都不做1000处理  只是请求落库存的时候需要乘1000
            BigDecimal d = QuantityFormatUtils.formatQuantityForDB(subTmp.getBaseUnitQuantity().trim());
            subOrder.setGoodsQuantity(d.longValue());
        } catch (Exception e) {
            log.error("格式化数量错误", e);
            throw new FacadeException("OTS-03-001-01-15-003", subTmp.getBatchId(), subTmp.getBaseUnitQuantity());
        }
        try {
            if (StringUtils.isBlank(SapAndCrmFieldDealUtils.generateIdField(subTmp.getProductionTime())) || StringUtils.isBlank(subTmp.getProductionTime())) {
                subOrder.setDom(DateFormatUtil.parse("20000101"));
            } else {
                //时间转换一下格式 sap发送的格式为yyyyMMdd
                subOrder.setDom(DateFormatUtil.parse(subTmp.getProductionTime()));
            }
        } catch (Exception e) {
            log.error("日期转换错误", e);
            throw new FacadeException("OTS-03-001-01-15-007");
        }
        Features subFeatures = Features.newFeatures(subTmp.getFeatures());
        subFeatures.addFeature("lineNo", subTmp.getOutLineId());
        subFeatures.addFeature("outSourceOrderLineId", subTmp.getOutSourceOrderLineId());
        subFeatures.addFeature("outDeliveryOrderLineId", subTmp.getOutDeliveryOrderLineId());
        subFeatures.addFeature("outAllocateOrderLineId", subTmp.getOutAllocateOrderLineId());
        subFeatures.addFeature("outDeliveryOrderId", mateial.getOutDeliveryOrderId());
        subFeatures.addFeature("goodsCode", subTmp.getScItemId());
        subFeatures.addFeature("saleUnitQuantity", subTmp.getSaleUnitQuantity());
        subFeatures.addFeature("saleUnit", subTmp.getSaleUnit());
        subFeatures.addFeature("baseUnit", subTmp.getBaseUnit());
        subFeatures.addFeature("baseUnitQuantity", subTmp.getBaseUnitQuantity());
        subFeatures.addFeature("isAllocate", outinFeatures.get("isAllocate"));
        subFeatures.addFeature("sourceWarehouingOrderCode", outinFeatures.get("sourceWarehouingOrderCode"));
        if (StringUtils.isNotBlank(subTmp.getExt2())) {
            subFeatures.addFeature("fullSapBatchNo", subTmp.getExt2());
        }
        subOrder.setOutSubOrderId(mateial.getOutMaterialCertificateId());
        subOrder.setDcSignal(subTmp.getDcSignal());
        subOrder.setSapType(subTmp.getSapType());
        if (StringUtils.isBlank(subTmp.getFactoryCode())) {
            throw new FacadeException("OTS-03-002-01-15-015");
        }
        WarehouseInfoDTO warehouseInfo = searchWarehouseByCode(subTmp, mateial, outinFeatures);
        subFeatures.addFeature("warehouseType", warehouseInfo.getType());
        //设置逻辑仓库类型 仓库的销售组织
        subOrder.setWarehouseAreaType(warehouseInfo.getType() == null ? null : warehouseInfo.getType().toString());
        subOrder.setWarehouseAreaOrgId(warehouseInfo.getSaleOrgId());
        if (StringUtils.isNotBlank(bizSubOrderId)){
            subFeatures.addFeature(BIZ_SUB_ORDER_ID,bizSubOrderId);
        }
        subOrder.setProperties(subFeatures.toString());
        //id是逻辑仓id  warehouseId是仓库id
        subOrder.setWarehouseAreaId(warehouseInfo.getId());
        subOrder.setWarehouseId(warehouseInfo.getWarehouseId());
        //如果是车销仓，没有parentCode，但是wms  库存那里又需要写warehouseId，就把warehouseAreaId赋值进去。
        //不然可能会报错
        if (StringUtils.isEmpty(warehouseInfo.getWarehouseId())) {
            subOrder.setWarehouseId(warehouseInfo.getId());
        }
        return subOrder;
    }

    /**
     * 拼接 查仓库的请求
     *
     * @param subTmp
     * @return
     */
    private WarehouseInfoDTO searchWarehouseByCode(MnSapIf004Detail subTmp) {
        /**
         * 物料凭证里过来的明细里的仓库  通过factoryCode（对应内部的仓库id）和inventoryCode匹配唯一仓库，  因为库存code 可能重复。
         * 1.有通过factoryCode inventoryCode 直接匹配仓库
         * 2.只有factoryCode 没有inventoryCode，通过只有factoryCode查到唯一下属的逻辑仓（如果有多个下属逻辑仓报错出去）
         */
        SearchCargoZoneListByDBRequest request = new SearchCargoZoneListByDBRequest();
        request.setSapFactoryCode(subTmp.getFactoryCode());
        if (StringUtils.isBlank(subTmp.getInventoryCode())) {
            request.setWarehouseCode("DEFAULT");
        } else {
            request.setWarehouseCode(subTmp.getInventoryCode());
        }
        SearchCargoZoneListByDBResponse response = unifiedInventoryService.searchCargoZoneListByDB(request);
        if (response == null || CollectionUtils.isEmpty(response.getResult())) {
            throw new FacadeException("OTS-03-001-01-15-001", "仓库Code:" + subTmp.getFactoryCode() + "库存地点code:" + subTmp.getInventoryCode());
        }
        WarehouseInfoDTO warehouseInfoDTO = new WarehouseInfoDTO();
        BeanUtils.copyProperties(response.getResult().get(0), warehouseInfoDTO);
        return warehouseInfoDTO;
    }

    private WarehouseInfoDTO searchWarehouseByCode(MnSapIf004Detail subTmp, MnSapIf004SDO mnSapIf004SDO, Features features) {
        /**
         * 物料凭证里过来的明细里的仓库  通过factoryCode（对应内部的仓库id）和inventoryCode匹配唯一仓库，  因为库存code 可能重复。
         * 1.有通过factoryCode inventoryCode 直接匹配仓库
         * 2.只有factoryCode 没有inventoryCode，通过只有factoryCode查到唯一下属的逻辑仓（如果有多个下属逻辑仓报错出去）
         */
        SearchCargoZoneListByDBRequest request = new SearchCargoZoneListByDBRequest();
        request.setSapFactoryCode(subTmp.getFactoryCode());
        if (StringUtils.isBlank(subTmp.getInventoryCode())) {
            request.setWarehouseCode("DEFAULT");
        } else {
            request.setWarehouseCode(subTmp.getInventoryCode());
        }
        SearchCargoZoneListByDBResponse response = unifiedInventoryService.searchCargoZoneListByDB(request);
        if (response == null || CollectionUtils.isEmpty(response.getResult())) {
            //当物料凭证包含外部调拨单号/销售单号 && 在订单共享平台也查到了调拨单/销售  && 根据工厂编码和库存地点找不到仓库时 打印日志
            if (!StringUtils.isAllBlank(mnSapIf004SDO.getOutAllocateOrderId(), mnSapIf004SDO.getOutSourceOrderId())
                    && !"0".equals(features.get("isAllocate"))) {
                log.error("warehouse_not_match,物料凭证号:{},外部调拨单或销售单号:{},factoryCode:{},warehouseCode:{}", mnSapIf004SDO.getOutMaterialCertificateId(),
                        StringUtils.isNotBlank(mnSapIf004SDO.getOutAllocateOrderId()) ? mnSapIf004SDO.getOutAllocateOrderId() : mnSapIf004SDO.getOutSourceOrderId(),
                        subTmp.getFactoryCode(), subTmp.getInventoryCode());
            }
            throw new FacadeException("OTS-03-001-01-15-001", "仓库Code:" + subTmp.getFactoryCode() + "库存地点code:" + subTmp.getInventoryCode());
        }
        WarehouseInfoDTO warehouseInfoDTO = new WarehouseInfoDTO();
        BeanUtils.copyProperties(response.getResult().get(0), warehouseInfoDTO);
        return warehouseInfoDTO;
    }

    private WarehouseInfoDTO searchWarehouse(QueryWarehouseByFactoryCodeAndWarehouseCodeRequest query) {
        Result<List<WarehouseInfoDTO>> listResult = forecastWarehouseService.queryWarehouseByFactoryCodeAndWarehouseCode(query);
        if (CollectionUtils.isEmpty(listResult.getResult())) {
            log.error("搜索库存，query=" + JSON.toJSONString(query));
            throw new FacadeException("OTS-03-001-01-15-001", "仓库Code:" + query.getFactoryCode() + "库存地点code:" + query.getWarehouseCode());
        }
        return listResult.getResult().get(0);
    }

    /**
     * 组装物料凭证主信息
     *
     * @param outin
     * @param sapMaterial
     * @return
     */
    private CreatOutinOrderRequest packageOutInOrderRequest(CreatOutinOrderRequest outin, MnSapIf004SDO sapMaterial, List<MnSapCommDetail> itemError) {
        Features features = Features.newFeatures(sapMaterial.getFeatures());
        outin.setOutWarehousingOrderId(sapMaterial.getOutMaterialCertificateId());
        features.addFeature("outSourceOrderId", sapMaterial.getOutSourceOrderId());
        features.addFeature("outAllocateOrderId", sapMaterial.getOutAllocateOrderId());
        features.addFeature("outDeliveryOrderId", sapMaterial.getOutDeliveryOrderId());
        features.addFeature("postingTime", sapMaterial.getPostingTime());
        features.addFeature("shipType", sapMaterial.getShipType());
        features.addFeature("shipNo", sapMaterial.getShipNo());
        features.addFeature("shipPhone", sapMaterial.getShipPhone());
        features.addFeature("shipContact", sapMaterial.getShipContact());
        features.addFeature("expectedDate", sapMaterial.getExpectedDate());
        features.addFeature("createTime", sapMaterial.getCreateTime());
        //是否工厂直发商家 是的话 要调用创建销售单的接口  只有调拨单考虑该选项。
        boolean isF2B = false;
        //判断是否是冲销类型， 是冲销类型，整个物料凭证都按冲销算.
        //且根据明细里的Ext字段查原物料凭证
        boolean isReverse = false;
        //0 无销售和调拨单号  1.是调拨单  2是销售单
        //销售 、调拨单号 要么2选1存在 要么都为空。 不会两个都有
        int isAllocate = 1;
        //2023.02.20 是否牛要客订单 是?库存操作完后不推进订单状态 返回 : 继续
        boolean isNyk = false;
        String sourceWarehouingOrderId = "";
        String sourceWarehouingOrderCode = "";
        String sourceWarehouseArea = "";
        String targetWarehouseArea = "";
        String sourceWarehouse = "";
        String targetWarehouse = "";
        String saleOrderTag = "normal";
        long sumQuantity = 0;
        //调拨/销售单据主键id
        String orderId = null;
        if (StringUtils.isBlank(sapMaterial.getOutAllocateOrderId()) && StringUtils.isBlank(sapMaterial.getOutSourceOrderId())) {
            isAllocate = 0;
            //如果是0 ，子单必须都有仓库号
        } else {
            /**
             *   1204 PD改需求， 有没有调拨/销售单或有调拨/销售单号无论能否查到  库存都落库  又调拨单/销售单号才通知
             *   如果销售调拨单号查到的销售调拨单是空的， isallocate按0处理
             */
            if (StringUtils.isNotBlank(sapMaterial.getOutAllocateOrderId())) {
                isAllocate = 1;
                AllocateOrderDetailsQueryResponse allocateResponse = getAllocateOrder(sapMaterial.getOutAllocateOrderId());
                //对象不为空，但是对象里面都是空的
                if (!checkAllocatedOrderNotBlank(allocateResponse)) {
                    isAllocate = 0;
                } else {
                    if (allocateResponse.getIsDTC() != null && allocateResponse.getIsDTC()) {
                        isF2B = true;
                    }
                    if (StringUtils.isNotBlank(allocateResponse.getNykId())) {
                        isNyk = true;
                    }
                    //出入库单 和调拨单互查 用自己的内部id
                    sourceWarehouingOrderId = String.valueOf(allocateResponse.getId());
                    sourceWarehouingOrderCode = allocateResponse.getCode();
                    //调拨退单要特殊处理，调拨退单的收发货仓和正向单据的收发货仓是一致的，所以实际执行退单的出入库时要把收发货仓反过来
                    if (BooleanUtils.isTrue(allocateResponse.getReturnOrder())) {
                        sourceWarehouseArea = allocateResponse.getReceiveAreaId();
                        targetWarehouseArea = allocateResponse.getShipAreaId();
                        sourceWarehouse = allocateResponse.getReceiveWarehouseId();
                        targetWarehouse = allocateResponse.getShipWarehouseId();
                    } else {
                        sourceWarehouseArea = allocateResponse.getShipAreaId();
                        targetWarehouseArea = allocateResponse.getReceiveAreaId();
                        sourceWarehouse = allocateResponse.getShipWarehouseId();
                        targetWarehouse = allocateResponse.getReceiveWarehouseId();
                    }
                    //业务单据号
                    features.addFeature(BIZ_ORDER_ID,allocateResponse.getCode());
                    orderId = allocateResponse.getId() == null ? null : allocateResponse.getId().toString();
                    features.addFeature(FULFILLMENT_TYPE_CODE,allocateResponse.getFulfillmentTypeCode());
                }
            }
            /**
             * SAP用outSourceOrderId给过来的外部id，可能是我们的销售单也可能是调拨单。
             * SAP用outAllocateOrderId给过来的外部id，肯定是调拨单。
             */
            if (StringUtils.isNotBlank(sapMaterial.getOutSourceOrderId())) {
                //查是否有对应的销售单
//                SaleOrderForMaterialRequest query = new SaleOrderForMaterialRequest();
//                query.setOuterOrderId(sapMaterial.getOutSourceOrderId());
//                Result<List<SaleOrderForMaterial>> result = otsApplicationAdapterSdkService.saleOrderForMaterial(query);
                OrderPageQueryRequest query = new OrderPageQueryRequest();
                query.setOuterOrderId(sapMaterial.getOutSourceOrderId());
                Result<List<OrderPageQueryResponse>> result = orderQueryService.pageQuery(query);

                //SaleOrderForMaterial saleOrderResponse = null;
                OrderPageQueryResponse saleOrderResponse = null;
                if (!CollectionUtils.isEmpty(result.getResult())) {
                    saleOrderResponse = result.getResult().get(0);
                    sourceWarehouingOrderId = String.valueOf(saleOrderResponse.getId());
                    sourceWarehouingOrderCode = saleOrderResponse.getOrderBizId();
                    sourceWarehouseArea = saleOrderResponse.getWareHouseCode();
                    /**
                     *    @see com.alibaba.citrus.ots.b2b.trade.order.facade.common.SetOrderTag
                     */
                    saleOrderTag = saleOrderResponse.getOrderTag();
                    isAllocate = 2;
                    features.addFeature(BIZ_ORDER_ID,saleOrderResponse.getOrderBizId());
                    orderId = saleOrderResponse.getId();
                }
                AllocateOrderDetailsQueryResponse allocateResponse = null;
                if (StringUtils.isBlank(sourceWarehouingOrderId)) {
                    //根据销售单查不到时 再查调拨单
                    //查是否有对应的调拨单
                    allocateResponse = getAllocateOrder(sapMaterial.getOutSourceOrderId());
                    //出入库单 和调拨单互查 用自己的内部id
                    if (checkAllocatedOrderNotBlank(allocateResponse)) {
                        isAllocate = 1;
                        if (StringUtils.isNotBlank(allocateResponse.getNykId())) {
                            isNyk = true;
                        }
                        sourceWarehouingOrderId = String.valueOf(allocateResponse.getId());
                        sourceWarehouingOrderCode = allocateResponse.getCode();
                        //这俩id是内部id
                        //调拨退单要特殊处理，调拨退单的收发货仓和正向单据的收发货仓是一致的，所以实际执行退单的出入库时要把收发货仓反过来
                        if (BooleanUtils.isTrue(allocateResponse.getReturnOrder())) {
                            sourceWarehouseArea = allocateResponse.getReceiveAreaId();
                            targetWarehouseArea = allocateResponse.getShipAreaId();
                            sourceWarehouse = allocateResponse.getReceiveWarehouseId();
                            targetWarehouse = allocateResponse.getShipWarehouseId();
                        } else {
                            sourceWarehouseArea = allocateResponse.getShipAreaId();
                            targetWarehouseArea = allocateResponse.getReceiveAreaId();
                            sourceWarehouse = allocateResponse.getShipWarehouseId();
                            targetWarehouse = allocateResponse.getReceiveWarehouseId();
                        }
                        if (allocateResponse.getIsDTC() != null && allocateResponse.getIsDTC()) {
                            isF2B = true;
                        }
                        features.addFeature(BIZ_ORDER_ID,allocateResponse.getCode());
                        orderId = allocateResponse.getId() == null ? null : allocateResponse.getId().toString();
                    }
                }
                /**
                 * 产品确定的，销售调拨单号无论是否查到 ，都落库
                 */
                if (saleOrderResponse == null && !checkAllocatedOrderNotBlank(allocateResponse)) {
                    isAllocate = 0;
                    // throw new FacadeException("OTS-03-001-15-006", sapMaterial.getOutSourceOrderId());
                }
            }
        }
        //在系统中查找调拨单 或销售单。  查不到。 将isAllocate置为0. 直接操作库存
        //查到调拨/销售单，将单子里的仓库-货区赋值到features里
        //SAP物料凭证里的明细里的仓库给的是code   调拨-销售单里存的是逻辑仓的id
        outin.setSourceWarehousingOrderId(sourceWarehouingOrderId);
        features.addFeature("isAllocate", isAllocate);
        features.addFeature("isF2B", isF2B);
        //是否牛要客订单
        features.addFeature("isNyk", isNyk);
        features.addFeature("saleOrderTag", saleOrderTag);
        features.addFeature("sourceWarehouingOrderCode", sourceWarehouingOrderCode);
        if (isAllocate > 0) {
            //当前明细要操作的仓库
            features.addFeature("sourceWarehouseArea", sourceWarehouseArea);
            features.addFeature("sourceWarehouse", sourceWarehouse);
            if (isAllocate == 1) {
                //有在途需要增加时，target里放目标仓库信息
                features.addFeature("targetWarehouseArea", targetWarehouseArea);
                features.addFeature("targetWarehouse", targetWarehouse);
            }
        }
//        //2022.05.18 这里加一个校验 如果参数里有 调拨单/销售单的外部单号 但是isAllocate是0的 我们需要重试
//        if (isAllocate == 0 && !StringUtils.isAllBlank(sapMaterial.getOutAllocateOrderId(),sapMaterial.getOutSourceOrderId())){
//            throw new FacadeException("OTS-03-002-01-15-112");
//        }
        Result<List<MaterialTypes>> materialResult = crUnifiedInventoryService.getMaterialTyps();
        List<MaterialTypes> materialTypesList = materialResult.getResult();
        Map<String, MaterialTypes> materialTypes = materialTypesList.stream()
                .collect(Collectors.toMap(x -> (x.getCodeType() + x.getOutInType()), x -> x, (v1, v2) -> v2));
        //先格式化一下物料凭证里的货品id 去一下0
        List<MnSapIf004Detail> itemsAfterFilter = sapMaterial.getItems().stream().filter(new Predicate<MnSapIf004Detail>() {
            @Override
            public boolean test(MnSapIf004Detail detail) {
                detail.setScItemId(SapAndCrmFieldDealUtils.subStringScItmCode(detail.getScItemId()));
                if (!StringUtils.isBlank(detail.getBatchId())) {
                    //批次id可能为空 在途的时候
                    detail.setBatchId(SapAndCrmFieldDealUtils.generateIdField(detail.getBatchId()));
                }
                if (!materialTypes.containsKey(detail.getSapType() + detail.getDcSignal())) {
                    //移动类型不识别的报错出去
                    throw new FacadeException("OTS-03-001-01-15-012", detail.getSapType() + ":" + detail.getDcSignal());
                }
                if (materialTypes.get(detail.getSapType() + detail.getDcSignal()).getInventoryType().intValue() == 1) {
                    log.warn("物料凭证明细在途跳过:物料凭证编号(" + sapMaterial.getOutMaterialCertificateId() + ")明细:(" + JSON.toJSONString(detail) + ")");
                    //在途的直接干掉不要了
                    return false;
                }

                return true;
            }
        }).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(itemsAfterFilter)) {
            //如果批次明细里都是在途的，直接返回，告诉SAP处理完了。 不继续往下走代码了
            return null;
        }

        List<CreateOutinOrderGoodsRequest> outinSubOrders = new ArrayList<>();
        Map<String, ScItemSDO> scItems = getGoodsList(itemsAfterFilter);
        //冲销时，原物料凭证单号
        String originoutMaterialCertificateId = "";
        //是否有入库类型的明细
        boolean hasInReverse = false;
        //获取到子单列表合集
        List<AllocateOrderDetail> allocateOrderDetailList = Lists.newArrayList();
        List<OrderLine> orderLineList = Lists.newArrayList();
        if (StringUtils.isNotBlank(orderId)){
            if (isAllocate ==1){
                AllocateOrderDetailLoadListRequest allocateOrderDetailLoadListRequest = new AllocateOrderDetailLoadListRequest();
                allocateOrderDetailLoadListRequest.setAllocateOrderId(Long.valueOf(orderId));
                allocateOrderDetailLoadListRequest.setSize(MAX_PAGE_SIZE);
                Result<List<AllocateOrderDetail>> listResult = allocateOrderDetailService.loadAllocateOrderDetailList(allocateOrderDetailLoadListRequest);
                if (listResult != null && CollectionUtils.isNotEmpty(listResult.getResult())){
                    allocateOrderDetailList.addAll(listResult.getResult());
                }
            }else if (isAllocate == 2){
                OrderLineLoadListRequest orderLineLoadListRequest = new OrderLineLoadListRequest();
                orderLineLoadListRequest.setMainOrderId(orderId);
                orderLineLoadListRequest.setLimit(MAX_PAGE_SIZE);
                Result<List<OrderLine>> result = orderLineService.loadOrderLineList(orderLineLoadListRequest);
                if (result != null && CollectionUtils.isNotEmpty(result.getResult())){
                    orderLineList.addAll(result.getResult());
                }
            }
        }
        for (MnSapIf004Detail subTmp : itemsAfterFilter) {
            /**
             * 组装物料凭证里的批次
             */
            String itemKey = subTmp.getItem_key();
            MnSapCommDetail mnSapCommDetail = new MnSapCommDetail();
            mnSapCommDetail.setItemKey(itemKey);
            mnSapCommDetail.setStatus("S");
            //2022-03-15 宸之新提需求，如果批次id为空，自定一套。 遇到的情况为电商仓的物流凭证没带批次id
            if (StringUtils.isBlank(subTmp.getBatchId())) {
                subTmp.setBatchId(defineBatchNo(subTmp.getScItemId(), subTmp.getFactoryCode(), subTmp.getInventoryCode(), subTmp.getProductionTime()));
            }
            try {
                checkParams(subTmp, materialTypes);
                //12.13新增入库可以冲销了， 入库类型的冲销只落库库存，不通知销售调拨单
                MaterialTypes types = materialTypes.get(subTmp.getSapType() + subTmp.getDcSignal());
                if (!hasInReverse) {
                    hasInReverse = (types.getMainOrderType().intValue() == 2)
                            && (types.getReverse().intValue() == 1);
                }
                if (!isReverse) {
                    //明细里有一条是是冲销类型，整个物料凭证都按冲销算
                    isReverse = (types.getReverse().intValue() == 1);
                }
                if (StringUtils.isNotBlank(subTmp.getExt())) {
                    originoutMaterialCertificateId = subTmp.getExt();
                }
                String bizSubOrderId = getBizSubOrderId(isAllocate, subTmp, allocateOrderDetailList, orderLineList);
                CreateOutinOrderGoodsRequest subOrder = packageItem(subTmp, outin, features, scItems, sapMaterial,bizSubOrderId);
                sumQuantity = sumQuantity + subOrder.getGoodsQuantity();
                outinSubOrders.add(subOrder);
            } catch (FacadeException e) {
                mnSapCommDetail.setMessage(e.getErrorMessage());
                mnSapCommDetail.setStatus("E");
                //这里就是阻断操作，不要乱动
                throw e;
            } finally {
                itemError.add(mnSapCommDetail);
            }
        }
        features.addFeature("sumQuantity", sumQuantity);
        features.addFeature("isReverse", isReverse);
        features.addFeature("hasInOrder", hasInReverse);
        if (isReverse) {
            features.addFeature("originoutMaterialCertificateId", originoutMaterialCertificateId);
        }
        EpochFeatures dtoFeature = new EpochFeatures();
        dtoFeature.setFeatureMap(features.getOriginMap());
        outin.setFeatures(dtoFeature);
        outin.setGoods(outinSubOrders);
        return outin;
    }

    /**
     * 自定义批次id
     *
     * @param goodsCode
     * @param factoryCode
     * @param warehouseCode
     * @return
     */
    private String defineBatchNo(String goodsCode, String factoryCode, String warehouseCode, String dom) {
        //EW 电商仓的缩写
        String defineBatchId = "EW" + goodsCode + factoryCode + warehouseCode;
        if (StringUtils.isBlank(dom)) {
            return defineBatchId;
        }
        return defineBatchId + dom;
    }

    /**
     * 校验批次的的不可为空的信息
     *
     * @param detail 批次信息
     * @return
     */
    private boolean checkParams(MnSapIf004Detail detail, Map<String, MaterialTypes> materialTypes) {
        if (StringUtils.isBlank(detail.getBatchId())) {
            //如果批次id为空， 并且不是在途类型的调拨。 则报错
            Integer inventoryType = materialTypes.get(detail.getSapType() + detail.getDcSignal()).getInventoryType();
            if (inventoryType == null) {
                throw new FacadeException("OTS-03-001-01-15-018");
            }
            if (inventoryType != null && inventoryType != 1) {
                throw new FacadeException("OTS-03-001-01-15-008");
            }
        }
        if (!materialTypes.containsKey(detail.getSapType() + detail.getDcSignal())) {
            throw new FacadeException("OTS-03-001-01-15-019");
        }
        if (StringUtils.isBlank(detail.getDcSignal())) {
            throw new FacadeException("OTS-03-001-01-15-009");
        }
        if (StringUtils.isBlank(detail.getSapType())) {
            throw new FacadeException("OTS-03-001-01-15-010");
        }
        //不校验日期了 如果日期为空 设置为2000年1月1日
//        if (StringUtils.isBlank(detail.getProductionTime())) {
//            throw new FacadeException("OTS-03-001-15-011");
//        }
//        if (StringUtils.isBlank(SapAndCrmFieldDealUtils.generateIdField(detail.getProductionTime()))) {
//            throw new FacadeException("OTS-03-001-15-014");
//        }
        return true;
    }

    /**
     * 获取货品列表，校验是否有该货品，并获取货品id，转换为出入库单创建参数时用
     *
     * @param items
     * @return
     */
    private Map<String, ScItemSDO> getGoodsList(List<MnSapIf004Detail> items) {
        Set<String> goodsCode = new HashSet<>();
        items.forEach(x -> goodsCode.add(x.getScItemId()));
//        SearchScItemRequest searchScItemRequest = new SearchScItemRequest();
//        Map<String, List<String>> outerIdList = new HashMap<>();
//        outerIdList.put("outerIdList", goodsCode.stream().collect(Collectors.toList()));
//        outerIdList.put("statusList", Arrays.asList("1","-1"));
//        searchScItemRequest.setPageSize(items.size());
//        searchScItemRequest.setSearchReqDTO(outerIdList);
//        Result<List<ScItemSDO>> scItemResult = baseDataService.searchScItem(searchScItemRequest);
        BaselineQueryScItemByPageFromDbRequest dbRequest = new BaselineQueryScItemByPageFromDbRequest();
        dbRequest.setOutIdList(goodsCode.stream().collect(Collectors.toList()));
        dbRequest.setStatusList(Lists.newArrayList("1", "-1"));
        dbRequest.setLimit(items.size());
        Object data = baseDataService.baselineQueryScItemByPageFromDb(dbRequest);
        Result<List<ScItemSDO>> scItemResult = convert(data);

        if (scItemResult == null) {
            throw new FacadeException("OTS-03-001-01-15-002", JSON.toJSONString(goodsCode));
        }
        if (CollectionUtils.isEmpty(scItemResult.getResult())) {
            throw new FacadeException("OTS-03-001-01-15-015", JSON.toJSONString(goodsCode));
        }
        Map<String, ScItemSDO> scItems = scItemResult.getResult().stream().collect(Collectors.toMap(x -> x.getOuterId(), x -> x, (v1, v2) -> v2));
        if (goodsCode.size() != scItemResult.getResult().size()) {
            Set<String> resultCode = new HashSet<>();
            scItemResult.getResult().forEach(x -> resultCode.add(x.getOuterId()));
            StringBuffer sb = new StringBuffer();
            items.forEach(x -> {
                if (!resultCode.contains(x.getScItemId())) {
                    sb.append(x.getScItemId() + ";");
                }
            });
            log.warn("MaterialHandle_GoodsCountNotMatch, scItemList:{}", sb.toString());
            throw new FacadeException("OTS-03-001-01-15-016", sb.toString());
        }
        return scItems;
    }

    //货品转换
    private Result<List<ScItemSDO>> convert(Object data) {
        ScItemFromDBResponse response = JSON.parseObject(JSON.toJSONString(data), ScItemFromDBResponse.class);
        List<ScItemSDO> scItemSDOS = Optional.ofNullable(response).map(ScItemFromDBResponse::getResult)
                .map(ScItemResult::getResult)
                .map(ScItemSearchResult::getSearchResult)
                .orElse(null);
        if (CollectionUtils.isEmpty(scItemSDOS)) {
            return null;
        }
        return Result.listSuccess(scItemSDOS, scItemSDOS.size());
    }


    /**
     * 获取调拨单
     *
     * @return
     */
    private AllocateOrderDetailsQueryResponse getAllocatedOrder(String outAllocateOrderId) {
        AllocateOrderQueryRequest allocateOrderQueryRequest = new AllocateOrderQueryRequest();
        allocateOrderQueryRequest.setOutAllocateOrderId(outAllocateOrderId);
        Result<AllocateOrderDetailsQueryResponse> allocateOrderResult = crUnifiedInventoryService.allocateOrderQuery(allocateOrderQueryRequest);
        AllocateOrderDetailsQueryResponse allocateResponse = allocateOrderResult.getResult();
        return allocateResponse;
    }

    /**
     * 获取调拨单
     *
     * @param outAllocateOrderId
     * @return
     */
    private AllocateOrderDetailsQueryResponse getAllocateOrder(String outAllocateOrderId) {
        AllocateOrderDetailsQueryResponse response = new AllocateOrderDetailsQueryResponse();
        AllocateOrderLoadListRequest loadListRequest = new AllocateOrderLoadListRequest();
        loadListRequest.setOutAllocateOrderId(outAllocateOrderId);
        Result<List<AllocateOrder>> listResult = allocateOrderService.loadAllocateOrderList(loadListRequest);
        if (listResult != null && CollectionUtils.isNotEmpty(listResult.getResult())) {
            fillAllocateOrderDetailsQueryResponse(response, listResult.getResult().get(0));
            return response;
        }
        loadListRequest = new AllocateOrderLoadListRequest();
        loadListRequest.setNykId(outAllocateOrderId);
        listResult = allocateOrderService.loadAllocateOrderList(loadListRequest);
        if (listResult != null && CollectionUtils.isNotEmpty(listResult.getResult())) {
            fillAllocateOrderDetailsQueryResponse(response, listResult.getResult().get(0));
        }
        return response;
    }

    //填充response
    private void fillAllocateOrderDetailsQueryResponse(AllocateOrderDetailsQueryResponse response, AllocateOrder allocateOrder) {
        response.setId(allocateOrder.getId());
        response.setCode(allocateOrder.getCode());
        response.setNykId(allocateOrder.getNykId());
        response.setReturnOrder(allocateOrder.getReturnOrder());
        response.setShipAreaId(allocateOrder.getShipAreaId());
        response.setShipWarehouseId(allocateOrder.getShipWarehouseId());
        response.setReceiveAreaId(allocateOrder.getReceiveAreaId());
        response.setReceiveWarehouseId(allocateOrder.getReceiveWarehouseId());
        response.setIsDTC(allocateOrder.getIsDTC());
        //履约方式
        response.setFulfillmentTypeCode(allocateOrder.getFulfillmentTypeCode());
    }


    private boolean checkAllocatedOrderNotBlank(AllocateOrderDetailsQueryResponse allocateResponse) {
        if (allocateResponse != null && allocateResponse.getId() != null) {
            return true;
        }
        return false;
    }

    /**
     * 通知调拨单修改数量
     */
    private void notifyAllocated(String sourceOrderId, List<AllocateOrderShippedScItemDTO> outList, List<AllocateOrderReceivedScItemDTO> inList, String outMaterialCertificateId, String postingTime, String sendTime) {
        if (outList.size() > 0) {
            Result<AllocateOrderShippedResponse> rep = allocateOrderWriteService.shipped(AllocateOrderShippedRequest.builder()
                    .id(sourceOrderId)
                    .outMaterialCertificateId(outMaterialCertificateId)
                    .shippedScItemDTOS(outList).postingTime(postingTime).sendTime(sendTime).build());
            log.warn("MnSapIf004ServiceImpl.externalSyncMaterial bcOtsService.notifyAllocated,request:{}", JSON.toJSONString(rep));
            if (!rep.isSuccess()) {
                throw new FacadeException(rep.getErrorCode(), rep.getMessage());
            }
        }
        if (inList.size() > 0) {
            Result<AllocateOrderReceivedResponse> rep = allocateOrderWriteService.received(AllocateOrderReceivedRequest.builder()
                    .id(sourceOrderId)
                    .outMaterialCertificateId(outMaterialCertificateId)
                    .receivedScItemDTOS(inList).postingTime(postingTime).sendTime(sendTime).build());
            if (!rep.isSuccess()) {
                throw new FacadeException(rep.getErrorCode(), rep.getMessage());
            }
        }
    }

    /**
     * 通知销售单修改数量
     */
    private SendOutGoodsRequest getNotifySaleOrderRequest(CreatOutinOrderRequest sapMaterial, String sendTime) {
        SendOutGoodsRequest sendOutGoodsRequest = new SendOutGoodsRequest();
        sendOutGoodsRequest.setOrderId(sapMaterial.getSourceWarehousingOrderId());
        sendOutGoodsRequest.setDeliveryOrderId(sapMaterial.getFeatures().getFeatureMap().get("outDeliveryOrderId"));
        sendOutGoodsRequest.setOutMaterialCertificateId(sapMaterial.getOutWarehousingOrderId());
        String postingTime = sapMaterial.getFeatures().getFeatureMap().get("postingTime");
        sendOutGoodsRequest.setPostingTime(postingTime);
        sendOutGoodsRequest.setSendTime(sendTime);
        List<Packagedetails> itemList = new ArrayList<>();
        List<CreateOutinOrderGoodsRequest> subOrderDOS = sapMaterial.getGoods();
        for (CreateOutinOrderGoodsRequest tmp : subOrderDOS) {
            Packagedetails packagedetails = new Packagedetails();
            Map<String, String> itemMap = new HashMap();
            //1201 张苏徽 让传参的key从货品id改为行号
            Features features = Features.newFeatures(tmp.getProperties());
            String linenoWithoutZero = SapAndCrmFieldDealUtils.generateIdField(features.get("outSourceOrderLineId"));
            //通知销售单 用销售单位数量， 别用基本单位数量。
            String notifyQuantity = features.get("saleUnitQuantity");
            itemMap.put(linenoWithoutZero, notifyQuantity);
            packagedetails.setOrderLineQuantity(itemMap);
            itemList.add(packagedetails);
        }
        sendOutGoodsRequest.setPackageDetailsList(itemList);
        return sendOutGoodsRequest;
    }

    /**
     * 创建完出入库单， 和库存变更完以后
     *
     * @param sapMaterial
     * @param inPilotScope
     */
    private void afterInventory(CreatOutinOrderRequest sapMaterial, String outMaterialCertificateId, String sendTime, boolean inPilotScope) {
        String isAllocate = sapMaterial.getFeatures().getFeatureMap().get("isAllocate");
        String postingTime = sapMaterial.getFeatures().getFeatureMap().get("postingTime");
        boolean dealOnwayInventory = ("1").equals(isAllocate);
        boolean isSaleOrder = ("2").equals(isAllocate);
        String isF2BStr = sapMaterial.getFeatures().getFeatureMap().get("isF2B");
        boolean isF2B = !StringUtils.isBlank(isF2BStr) && Boolean.parseBoolean(isF2BStr);
        //判断是否是牛要客订单
        String isNykStr = sapMaterial.getFeatures().getFeatureMap().get("isNyk");
        boolean isNyk = StringUtils.isNotBlank(isNykStr) && Boolean.parseBoolean(isNykStr);
        if (isSaleOrder && !inPilotScope) {

            SendOutGoodsRequest request = getNotifySaleOrderRequest(sapMaterial, sendTime);
            Result<DeliveryOrderModifyResponse> resp = sandOutGoodsService.sandOutGoods(request);
            if (!resp.isSuccess()) {
                throw new FacadeException(resp.getErrorCode(), resp.getMessage());
            }
        }
        String fulfillmentTypeCode = sapMaterial.getFeatures().getFeatureMap().get(FULFILLMENT_TYPE_CODE);
        //不需要推进调拨单状态
        boolean isNotNeedPushStatus = Arrays.asList(FulfillmentTypeEnum.PILOT2PILOT,FulfillmentTypeEnum.PILOT2CURRENT,FulfillmentTypeEnum.CURRENT2PILOT,FulfillmentTypeEnum.VIRTUAL_TRANSFER).stream()
                .anyMatch(fulfillmentTypeEnum -> fulfillmentTypeEnum.getValue().toString().equals(fulfillmentTypeCode));
        if (dealOnwayInventory && !isNyk && !isNotNeedPushStatus) {
            List<CreateOutinOrderGoodsRequest> subOrderDOS = sapMaterial.getGoods();
            Result<List<MaterialTypes>> materialResult = crUnifiedInventoryService.getMaterialTyps();
            List<MaterialTypes> materialTypesList = materialResult.getResult();
            List<ScItemDetail> createSaleList = getCreateSaleList(materialTypesList, subOrderDOS, isF2B);
            List<AllocateOrderShippedScItemDTO> outlist = getOutList(materialTypesList, subOrderDOS, isAllocate);
            List<AllocateOrderReceivedScItemDTO> inList = getInList(materialTypesList, subOrderDOS, isAllocate);
            notifyAllocated(sapMaterial.getSourceWarehousingOrderId(), outlist, inList, outMaterialCertificateId, postingTime, sendTime);
            if (isF2B && createSaleList.size() > 0) {
                //创建销售单 新增需要传一下逻辑仓id
                String warehouseAreaId = subOrderDOS.get(0).getWarehouseAreaId();
                notifyCreateSaleOrder(sapMaterial.getSourceWarehousingOrderId(), createSaleList, warehouseAreaId, outMaterialCertificateId);
            }
        }
    }

    /**
     * 创建通知调拨单用的list
     *
     * @param subOrderDOS
     * @param isAllocate
     * @return
     */
    private List<AllocateOrderShippedScItemDTO> getOutList(List<MaterialTypes> materialTypesList, List<CreateOutinOrderGoodsRequest> subOrderDOS, String isAllocate) {
        List<AllocateOrderShippedScItemDTO> outList = new ArrayList<>();
        for (CreateOutinOrderGoodsRequest tmp : subOrderDOS) {
            MaterialTypes materialTypes = getMatrilaTypeByCodeAndType(materialTypesList, tmp.getSapType(), tmp.getDcSignal());
            if (materialTypes.getMainOrderType() == 2) {
                if ("1".equals(isAllocate)) {
                    AllocateOrderShippedScItemDTO outDto = new AllocateOrderShippedScItemDTO();
                    outDto.setScItemCode(tmp.getGoodsId());
                    outDto.setScItemId(tmp.getGoodsId());
                    //前面位了落库存 乘了1000处理。 这里要除回去
                    outDto.setShipQuantity(QuantityFormatUtils.getQuantityForPage(tmp.getGoodsQuantity().toString()).toString());
                    //加上外部单号
                    outDto.setOutAllocateOrderLineId(StringUtils.isBlank(tmp.getOutAllocateOrderLineId()) ?
                            tmp.getOutSourceOrderLineId() : tmp.getOutAllocateOrderLineId());
                    outList.add(outDto);
                }
            }
        }
        return outList;
    }

    private List<AllocateOrderCancelShippedScItemDTO> getReverseOutList(List<MaterialTypes> materialTypesList, List<CreateOutinOrderGoodsRequest> subOrderDOS, String isAllocate) {
        List<AllocateOrderCancelShippedScItemDTO> outList = new ArrayList<>();
        for (CreateOutinOrderGoodsRequest tmp : subOrderDOS) {
            MaterialTypes materialTypes = getMatrilaTypeByCodeAndType(materialTypesList, tmp.getSapType(), tmp.getDcSignal());
            if (materialTypes.getMainOrderType() == 1) {
                if ("1".equals(isAllocate)) {
                    AllocateOrderCancelShippedScItemDTO outDto = new AllocateOrderCancelShippedScItemDTO();
                    outDto.setScItemCode(tmp.getGoodsId());
                    outDto.setScItemId(tmp.getGoodsId());
                    //前面位了落库存 乘了1000处理。 这里要除回去
                    outDto.setShipQuantity(QuantityFormatUtils.getQuantityForPage(tmp.getGoodsQuantity().toString()).toString());
                    outDto.setOutAllocateOrderLineId(StringUtils.isBlank(tmp.getOutAllocateOrderLineId()) ?
                            tmp.getOutSourceOrderLineId() : tmp.getOutAllocateOrderLineId());
                    outList.add(outDto);
                }
            }
        }
        return outList;
    }

    private List<AllocateOrderReceivedScItemDTO> getReverseInList(List<MaterialTypes> materialTypesList, List<CreateOutinOrderGoodsRequest> subOrderDOS, String isAllocate) {
        List<AllocateOrderReceivedScItemDTO> inList = new ArrayList<>();
        for (CreateOutinOrderGoodsRequest tmp : subOrderDOS) {
            MaterialTypes materialTypes = getMatrilaTypeByCodeAndType(materialTypesList, tmp.getSapType(), tmp.getDcSignal());
            if (materialTypes.getMainOrderType() == 3) {
                //忽略 不变更库存。
                continue;
            }
            if (materialTypes.getMainOrderType() == 2) {
                if ("1".equals(isAllocate)) {
                    AllocateOrderReceivedScItemDTO outDto = new AllocateOrderReceivedScItemDTO();
                    outDto.setScItemCode(tmp.getGoodsId());
                    outDto.setScItemId(tmp.getGoodsId());
                    //前面位了落库存 乘了1000处理。 这里要除回去
                    outDto.setReceiveQuantity(QuantityFormatUtils.getQuantityForPage(tmp.getGoodsQuantity().toString()).toString());
                    //外部行号
                    outDto.setOutAllocateOrderLineId(StringUtils.isBlank(tmp.getOutAllocateOrderLineId()) ?
                            tmp.getOutSourceOrderLineId() : tmp.getOutAllocateOrderLineId());
                    inList.add(outDto);
                }
            }
        }
        return inList;
    }

    /**
     * 创建通知调拨单用的list
     *
     * @param subOrderDOS
     * @param isAllocate
     * @return
     */
    private List<AllocateOrderReceivedScItemDTO> getInList(List<MaterialTypes> materialTypesList, List<CreateOutinOrderGoodsRequest> subOrderDOS, String isAllocate) {
        List<AllocateOrderReceivedScItemDTO> inList = new ArrayList<>();
        for (CreateOutinOrderGoodsRequest tmp : subOrderDOS) {
            MaterialTypes materialTypes = getMatrilaTypeByCodeAndType(materialTypesList, tmp.getSapType(), tmp.getDcSignal());
            if (materialTypes.getMainOrderType() == 3) {
                //忽略 不变更库存。
                continue;
            }
            if (materialTypes.getMainOrderType() == 1) {
                //加库存
                if (("1").equals(isAllocate)) {
                    AllocateOrderReceivedScItemDTO inDto = new AllocateOrderReceivedScItemDTO();
                    inDto.setScItemCode(tmp.getGoodsId());
                    inDto.setScItemId(tmp.getGoodsId());
                    //前面位了落库存 乘了1000处理。 这里要除回去
                    inDto.setReceiveQuantity(QuantityFormatUtils.getQuantityForPage(tmp.getGoodsQuantity().toString()).toString());
                    //外部调拨单行号
                    inDto.setOutAllocateOrderLineId(StringUtils.isBlank(tmp.getOutAllocateOrderLineId()) ?
                            tmp.getOutSourceOrderLineId() : tmp.getOutAllocateOrderLineId());
                    inList.add(inDto);
                }
            }
        }
        return inList;
    }

    /**
     * 创建销售单用的list
     *
     * @param subOrderDOS
     * @param isF2B
     * @return
     */
    private List<ScItemDetail> getCreateSaleList(List<MaterialTypes> materialTypesList, List<CreateOutinOrderGoodsRequest> subOrderDOS, boolean isF2B) {
        List<ScItemDetail> createSaleList = new ArrayList<>();
        for (CreateOutinOrderGoodsRequest tmp : subOrderDOS) {
            MaterialTypes materialTypes = getMatrilaTypeByCodeAndType(materialTypesList, tmp.getSapType(), tmp.getDcSignal());
            if (materialTypes.getMainOrderType() == 1) {
                //加库存
                if (isF2B) {
                    ScItemDetail scItemDetail = new ScItemDetail();
                    Features features = Features.newFeatures(tmp.getProperties());
                    scItemDetail.setScItemId(tmp.getGoodsId());
                    scItemDetail.setSaleUnit(features.get("saleUnit"));
                    scItemDetail.setBaseUnitQuantity(features.get("baseUnitQuantity"));
                    scItemDetail.setBaseUnit(features.get("baseUnit"));
                    scItemDetail.setSaleUnitQuantity(features.get("saleUnitQuantity"));
                    createSaleList.add(scItemDetail);
                }
            }
        }
        return createSaleList;
    }

    /**
     * 根据物料凭证里的类型 获取是加还是减库存
     *
     * @param list
     * @param codeType
     * @param outinType
     * @return
     */
    public MaterialTypes getMatrilaTypeByCodeAndType(List<MaterialTypes> list, String codeType, String outinType) {
        for (MaterialTypes materialTypeDesc : list) {
            if (materialTypeDesc.getCodeType().trim().equals(codeType.trim()) && materialTypeDesc.getOutInType().equalsIgnoreCase(outinType)) {
                return materialTypeDesc;
            }
        }
        throw new FacadeException("OTS-03-001-01-15-020", codeType + ":" + outinType);
    }

    /**
     * 通知创建销售单
     *
     * @param allocateId
     * @param createSaleList
     */
    public void notifyCreateSaleOrder(String allocateId, List<ScItemDetail> createSaleList, String warehouseAreaId, String outMaterialCertificateId) {
        GenerateOrderRequest generateOrderRequest = new GenerateOrderRequest();
        generateOrderRequest.setAllocateId(allocateId);
        generateOrderRequest.setScItemList(createSaleList);
        generateOrderRequest.setWareHouseAreaId(warehouseAreaId);
        generateOrderRequest.setOutMaterialCertificateId(outMaterialCertificateId);
        Result<Void> result = orderWriteService.generateOrder(generateOrderRequest);
        if (!result.isSuccess()) {
            throw new FacadeException(result.getErrorCode(), result.getMessage());
        }
    }

    /**
     * 冲销出入库单
     * 入库的冲销 不通知销售调拨单
     * isInReverse ture:入库的冲销，不通知调拨销售变更状态
     */
    public void reverseOutinOrder(CreatOutinOrderRequest outin, MnSapIf004SDO sapMaterial, boolean isInReverse, MnSapIf004Request mnCrmIf004Request) {
        //找不到原单也冲销，代码先注释，后期如果需要再放开
        //String deliveryOrderId = sapMaterial.getOutDeliveryOrderId();
//        GetOrigionOutinOrderRequest request = new GetOrigionOutinOrderRequest();
//        request.setDeliveryOrderId(deliveryOrderId);
//        Result<List<OutinOrderForReverseResp>> originOutinOrder = bcOtsService.getOrigionOutinOrder(request);
//        if (!originOutinOrder.isSuccess()) {
//            throw new FacadeException("OTS-03-002-15-018");
//        }
        log.warn("MnSapIf004ServiceImpl.externalSyncMaterial bcOtsService.reverseOutinOrder,request:{}", JSON.toJSONString(outin));
        Result<WarehousingOrderOperationResponse> result = bcOtsService.reverseOutinOrder(outin);
        log.warn("MnSapIf004ServiceImpl.externalSyncMaterial bcOtsService.reverseOutinOrder,request:{}", result == null ? "" : JSON.toJSONString(result));

        if (result.isSuccess()) {
            //通知调拨 销售单 改状态
            String isAllocate = outin.getFeatures().getFeatureMap().get("isAllocate");
            boolean dealOnwayInventory = ("1").equals(isAllocate);
            boolean isSaleOrder = ("2").equals(isAllocate);
            String isSaleChangeOrder = outin.getFeatures().getFeatureMap().get("saleOrderTag");
            //判断是否是牛要客订单
            String isNykStr = outin.getFeatures().getFeatureMap().get("isNyk");
            boolean isNyk = StringUtils.isNotBlank(isNykStr) && Boolean.parseBoolean(isNykStr);
            String sendTime = Optional.ofNullable(mnCrmIf004Request).map(MnSapIf004Request::getMessageHeader).map(MessageHeaderSDO::getSendTime).orElse(null);
            if (isSaleOrder) {
                //入库且是换货类型的 不通知销售单
                /**
                 * exchange  在其他应用的参数模型里，不好引用~
                 *  @see com.alibaba.citrus.ots.b2b.trade.order.facade.common.SetOrderTag
                 */
                if (!("exchange".equals(isSaleChangeOrder) && isInReverse)) {
                    SendOutGoodsRequest updateSaleOrderRequest = getNotifySaleOrderRequest(outin, sendTime);
                    log.warn("MnSapIf004ServiceImpl.externalSyncMaterial sandOutGoodsService.chargeAgainstGoods,request:{}", JSON.toJSONString(updateSaleOrderRequest));
                    Result<DeliveryOrderModifyResponse> responseResult = sandOutGoodsService.chargeAgainstGoods(updateSaleOrderRequest);
                    log.warn("MnSapIf004ServiceImpl.externalSyncMaterial sandOutGoodsService.chargeAgainstGoods,request:{}", responseResult == null ? "" : JSON.toJSONString(responseResult));
                    if (!responseResult.isSuccess()) {
                        throw new FacadeException("OTS-03-002-01-15-008", responseResult.getMessage());
                    }
                }
            }
            //是调拨单 且非牛要客订单
            if (dealOnwayInventory && !isNyk) {
                List<CreateOutinOrderGoodsRequest> subOrderDOS = outin.getGoods();
                String postingTime = outin.getFeatures().getFeatureMap().get("postingTime");
                Result<List<MaterialTypes>> materialResult = crUnifiedInventoryService.getMaterialTyps();
                List<MaterialTypes> materialTypesList = materialResult.getResult();
                List<AllocateOrderCancelShippedScItemDTO> outlist = getReverseOutList(materialTypesList, subOrderDOS, isAllocate);
                List<AllocateOrderReceivedScItemDTO> inlist = getReverseInList(materialTypesList, subOrderDOS, isAllocate);
                if (outlist.size() > 0) {
                    AllocateOrderCancelShippedRequest request = AllocateOrderCancelShippedRequest.builder()
                            .id(outin.getSourceWarehousingOrderId())
                            .outMaterialCertificateId(sapMaterial.getOutMaterialCertificateId()).postingTime(postingTime)
                            .sendTime(sendTime)
                            .shippedScItemDTOS(outlist).build();
                    log.warn("MnSapIf004ServiceImpl.externalSyncMaterial allocateOrderWriteService.cancelShipped,request:{}", JSON.toJSONString(request));
                    Result<AllocateOrderCancelShippedResponse> resp = allocateOrderWriteService.cancelShipped(request);
                    log.warn("MnSapIf004ServiceImpl.externalSyncMaterial allocateOrderWriteService.cancelShipped,resp:{}", resp == null ? "" : JSON.toJSONString(resp));
                    if (!resp.isSuccess()) {
                        throw new FacadeException("OTS-03-002-01-15-004", resp.getMessage());
                    }
                }
                if (inlist.size() > 0) {
                    AllocateOrderCancelReceivedRequest request = AllocateOrderCancelReceivedRequest.builder()
                            .id(outin.getSourceWarehousingOrderId())
                            .outMaterialCertificateId(sapMaterial.getOutMaterialCertificateId()).postingTime(postingTime)
                            .sendTime(sendTime)
                            .receivedScItemDTOS(inlist).build();
                    log.warn("MnSapIf004ServiceImpl.externalSyncMaterial allocateOrderWriteService.cancelReceived,request:{}", JSON.toJSONString(request));
                    Result<AllocateOrderCancelReceivedResponse> resp = allocateOrderWriteService.cancelReceived(request);
                    log.warn("MnSapIf004ServiceImpl.externalSyncMaterial allocateOrderWriteService.cancelReceived,resp:{}", resp == null ? "" : JSON.toJSONString(resp));
                    if (!resp.isSuccess()) {
                        throw new FacadeException("OTS-03-002-01-15-009", resp.getMessage());
                    }
                }
            }
        }
        if (!result.isSuccess()) {
            saveRetryLogs(sapMaterial.getOutMaterialCertificateId(), JSON.toJSONString(mnCrmIf004Request),
                    StringUtils.isNotBlank(result.getMessage()) ? result.getMessage() : "冲销库存操作失败");
            throw new FacadeException("OTS-03-002-01-15-012", "请前往系统日志搜索并重试接口");
        }
        //处理004早于008 013的
        if (result.isSuccess()) {
            if (isBeforeSourceOrder(outin, sapMaterial)) {
                saveMaterialRetry(mnCrmIf004Request, sapMaterial);
            } else {
                updateMaterialRetry(mnCrmIf004Request, sapMaterial);
            }
        }
    }

    /**
     * 保存需要重试的物料凭证信息，通过materialId进行幂等，同一个materialId只创建一次
     *
     * @param materialId
     * @param json
     */
    private void saveRetryLogs(String materialId, String json, String msg) {
        GetFailRecordBySAPMertrailIdRequest queryRequest = new GetFailRecordBySAPMertrailIdRequest();
        queryRequest.setOutMaterialCertificateId(materialId);
        try {
            //如果已经存在，则不需要创建重试记录
            Result<List<MaterialRetry>> result = retryService.getFailRecordBySAPMertrailId(queryRequest);
            if (CollectionUtils.isNotEmpty(result.getResult())) {
                if (StringUtils.isNotBlank(msg)) {
                    MaterialRetry materialRetry = result.getResult().get(0);
                    materialRetry.setMsg(msg);
                    retryService.update(materialRetry);
                }
                return;
            }
        } catch (Exception e) {
            log.error("saveRetryLogs query: {} failed", materialId);
        }

        MaterialRetry materialRetry = new MaterialRetry();
        materialRetry.setOutMaterialCertificateId(materialId);
        materialRetry.setRequestJson(json);
        if (StringUtils.isNotBlank(msg)) {
            materialRetry.setMsg(msg);
        }
        retryService.createFailMaterial(materialRetry);
    }

    //更新物料凭证的重试
    private void updateMaterialRetry(MnSapIf004Request request, MnSapIf004SDO sdo) {
        try {
            String outMaterialCertificateId = sdo.getOutMaterialCertificateId();
            String outSourceOrderId = sdo.getOutSourceOrderId();
            String outAllocateOrderId = sdo.getOutAllocateOrderId();
            if (StringUtils.isAllBlank(outSourceOrderId, outAllocateOrderId)) {
                return;
            }
            MaterialSyncRetryLoadListRequest materialSyncRetryLoadListRequest = new MaterialSyncRetryLoadListRequest();
            materialSyncRetryLoadListRequest.setOutMaterialCertificateId(outMaterialCertificateId);
            materialSyncRetryLoadListRequest.setStatus(0);
            Result<List<MaterialSyncRetry>> listResult = materialSyncRetryService.loadList(materialSyncRetryLoadListRequest);
            if (listResult != null && CollectionUtils.isNotEmpty(listResult.getResult())) {
                for (MaterialSyncRetry materialSyncRetry : listResult.getResult()) {
                    materialSyncRetry.setStatus(1);
                    materialSyncRetryService.update(materialSyncRetry);
                }
            }
        } catch (Throwable e) {
            log.error("MnSapIf004ServiceImpl_updateMaterialRetry_error", e);
        }
    }

    /**
     * 004先来的写入临时表 物料凭证唯一
     *
     * @param request
     * @param sdo
     */
    private void saveMaterialRetry(MnSapIf004Request request, MnSapIf004SDO sdo) {
        try {
            String outMaterialCertificateId = sdo.getOutMaterialCertificateId();
            String outSourceOrderId = sdo.getOutSourceOrderId();
            String outAllocateOrderId = sdo.getOutAllocateOrderId();
            if (StringUtils.isAllBlank(outSourceOrderId, outAllocateOrderId)) {
                return;
            }
            MaterialSyncRetryLoadListRequest materialSyncRetryLoadListRequest = new MaterialSyncRetryLoadListRequest();
            materialSyncRetryLoadListRequest.setOutMaterialCertificateId(outMaterialCertificateId);
            Result<List<MaterialSyncRetry>> listResult = materialSyncRetryService.loadList(materialSyncRetryLoadListRequest);
            if (listResult != null && CollectionUtils.isNotEmpty(listResult.getResult())) {
                return;
            }
            MaterialSyncRetry materialSyncRetry = new MaterialSyncRetry();
            materialSyncRetry.setOutMaterialCertificateId(outMaterialCertificateId);
            materialSyncRetry.setOutOrderId(StringUtils.isBlank(outSourceOrderId) ? outAllocateOrderId : outSourceOrderId);
            materialSyncRetry.setStatus(0);
            materialSyncRetry.setRequestParams(JSON.toJSONString(request));
            materialSyncRetryService.create(materialSyncRetry);
        } catch (Throwable e) {
            log.error("MnSapIf004ServiceImpl_saveMaterialRetry_error", e);
        }

    }

    /**
     * TODO 判断是否需要操作在途
     * 是一盘货订单 && 目的仓在试点范围内
     */
    private void changeTransitInventoryQuantity(List<MnSapIf004SDO> dataList,CreatOutinOrderRequest outInRequest) {
        try {
            //一盘货调拨单 + /-
            log.info("changeTransitInventoryQuantity_dataList={}, outInRequest:{}", JSON.toJSONString(dataList),JSON.toJSONString(outInRequest));
            Map<String, String> featureMap = MapUtils.emptyIfNull(outInRequest.getFeatures().getFeatureMap());
            String isAllocate = featureMap.get("isAllocate");
            if (!"1".equals(isAllocate)){
                //非调拨单
                log.info("changeTransitInventoryQuantity_not_allocate_order");
                return;
            }
            //目标仓在试点范围内
            String targetWarehouseArea = featureMap.get("targetWarehouseArea");
            if (StringUtils.isBlank(targetWarehouseArea)){
                log.info("changeTransitInventoryQuantity_no_targetWarehouseArea");
                return;
            }
            SearchCargoZoneListByDBRequest searchCargoZoneListByDBRequest = new SearchCargoZoneListByDBRequest();
            searchCargoZoneListByDBRequest.setWarehouseAreaIds(Lists.newArrayList(targetWarehouseArea));
            SearchCargoZoneListByDBResponse dbResponse = unifiedInventoryService.searchCargoZoneListByDB(searchCargoZoneListByDBRequest);
            if (dbResponse == null || CollectionUtils.isEmpty(dbResponse.getResult())){
                log.error("changeTransitInventoryQuantity_searchCargoZoneListByDB_empty");
                return;
            }
            WarehouseInfo warehouseInfo = dbResponse.getResult().get(0);
            boolean inPilotScope = isInPilotScope(warehouseInfo.getCode());
            if (!inPilotScope){
                log.info("changeTransitInventoryQuantity_not_inPilotScope");
                return;
            }
            //出入库方式
            List<MaterialTypes> materialTypes = JSON.parseArray(MATERIAL_TYPE, MaterialTypes.class);
            MnSapIf004Detail mnSapIf004Detail = dataList.get(0).getItems().get(0);
            Integer mainOrderType = materialTypes.stream().filter(type -> Objects.equals(type.getCodeType(), mnSapIf004Detail.getSapType())
                    && Objects.equals(type.getOutInType(), mnSapIf004Detail.getDcSignal())).map(MaterialTypes::getMainOrderType).findFirst().orElse(null);
            if (!OUT.equals(mainOrderType)){
                log.error("mainOrderType_not_match");
                return;
            }

            for (MnSapIf004SDO mnSapIf004SDO : dataList) {
                ChangeTransitInventoryQuantityRequest request = new ChangeTransitInventoryQuantityRequest();
                request.setOutAllocateOrderId(mnSapIf004SDO.getOutAllocateOrderId());
                request.setOutSourceOrderId(mnSapIf004SDO.getOutSourceOrderId());
                request.setOutMaterialCertificateId(mnSapIf004SDO.getOutMaterialCertificateId());
                List<TransitInventoryDetail> detailList = new ArrayList<>();
                Map<String, ScItemSDO> scItemSDOMap = getGoodsList(mnSapIf004SDO.getItems());
                for (MnSapIf004Detail item : mnSapIf004SDO.getItems()) {
//                    WarehouseInfoDTO warehouseInfoDTO = searchWarehouseByCode(item);
                    TransitInventoryDetail transitInventoryDetail = new TransitInventoryDetail();
                    transitInventoryDetail.setOutAllocateOrderLineId(item.getOutAllocateOrderLineId());
                    transitInventoryDetail.setOutSourceOrderLineId(item.getOutSourceOrderLineId());
                    transitInventoryDetail.setGoodsId(scItemSDOMap.get(item.getScItemId()).getScItemId());
                    transitInventoryDetail.setGoodsName(scItemSDOMap.get(item.getScItemId()).getTitle());
                    transitInventoryDetail.setLogicWarehouseId(warehouseInfo.getId());
//                    transitInventoryDetail.setLogicWarehouseId(warehouseInfoDTO.getId());
                    transitInventoryDetail.setLogicWarehouseName(warehouseInfo.getName());
//                    transitInventoryDetail.setLogicWarehouseName(warehouseInfoDTO.getName());
                    transitInventoryDetail.setWarehouseId(warehouseInfo.getWarehouseId());
//                    transitInventoryDetail.setWarehouseId(warehouseInfoDTO.getWarehouseId());
//                    transitInventoryDetail.setWarehouseName();
                    transitInventoryDetail.setFactoryCode(item.getFactoryCode());
                    transitInventoryDetail.setInventoryCode(StringUtils.isEmpty(item.getInventoryCode()) ? "DEFAULT" : item.getInventoryCode());
                    //如果批次号和生产日期为空，就取当前日期
                    if (StringUtils.isBlank(item.getBatchId())) {
                        String now = LocalDate.now(ZoneId.of("Asia/Shanghai")).toString().replace("-", "");
                        item.setBatchId(now);
                    }
                    transitInventoryDetail.setBatchNo(item.getBatchId());
                    if (StringUtils.isBlank(item.getProductionTime())) {
                        item.setProductionTime(item.getBatchId());
                    }
                    transitInventoryDetail.setProductDate(DateFormatUtil.parse(item.getProductionTime()));
                    if (StringUtils.isBlank(item.getBaseUnitQuantity())) {
                        transitInventoryDetail.setQuantity(0);
                    } else {
                        transitInventoryDetail.setQuantity(QuantityFormatUtils.formatQuantityForDB(item.getBaseUnitQuantity().trim()).intValue());
                    }
                    transitInventoryDetail.setSapType(item.getSapType());
                    transitInventoryDetail.setDcSignal(item.getDcSignal());
                    transitInventoryDetail.setOrderType("1");
                    //                transitInventoryDetail.setBusinessType();
                    //                transitInventoryDetail.setFromSys();
                    transitInventoryDetail.setUnit(item.getBaseUnit());
                    transitInventoryDetail.setOutLineId(item.getOutLineId());
                    transitInventoryDetail.setFromSys("APP_SAP");
                    transitInventoryDetail.setBusinessType("11");
                    detailList.add(transitInventoryDetail);
                }
                request.setDetailList(detailList);
                Result<Boolean> result = bcOtsService.changeTransitInventoryQuantity(request);
                if (!result.isSuccess() || !result.getResult()) {
                    log.info("changeTransitInventoryQuantity.fail={}", JSON.toJSONString(request));
                }
            }
        } catch (Exception e) {
            //失败只记录异常，不影响原有接口功能
            log.error("changeTransitInventoryQuantity.error={}", e);
        }
    }


    /**
     * 更新计划单修改
     * sapType:生产入库	703	生产入库	S
     * sapType:生产入库冲销	704	生产入库冲销	H
     * dcSignal:是否冲销  (S:非冲销；H:冲销)
     *
     * @param dataList list
     */
    private void updatePlanOrder(List<MnSapIf004SDO> dataList) {
//        mnSapIf004Detail.getDcSignal();// 是否冲销  (S:非冲销 累加；H:冲销 累间)
//        mnSapIf004Detail.getSapType(); // 移动类型 703 704
//        mnSapIf004Detail.getFactoryCode(); // 逻辑工厂仓
//        mnSapIf004Detail.getScItemId(); // 货品
//        mnSapIf004Detail.getProductionTime(); // 生产日期
//        mnSapIf004Detail.getSaleUnit(); // 单位
//        mnSapIf004Detail.getBaseUnitQuantity();// 基本单位数量 基本单位数量
//        mnSapIf004Detail.getSaleUnitQuantity();// 销售单位数量
//      工厂+货品+生产日期+单位
//      工厂+货品+计划日期+单位
        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }
        try {
            for (MnSapIf004SDO mnSap : dataList) {
                List<MnSapIf004Detail> items = mnSap.getItems();
                if (CollectionUtils.isEmpty(items)) {
                    continue;
                }
                for (MnSapIf004Detail mn : items) {
                    // 移动类型 703 704
                    if (Objects.equals(mn.getSapType(), "703") || Objects.equals(mn.getSapType(), "704")) {
                        log.info("externalSyncMaterial_updatePlanOrder request:{}", JSON.toJSON(mn));
//                        BusinessPlanOrderUpdateRequest businessPlanOrderUpdateRequest = new BusinessPlanOrderUpdateRequest();
                        BusinessPlanOrderUpdateRequest updateRequest = new BusinessPlanOrderUpdateRequest();
                        // 是否冲销  (S:非冲销；H:冲销)
                        updateRequest.setDcSignal(mn.getDcSignal());
                        if (mn.getDcSignal() == null) {
                            continue;
                        }
                        // 逻辑工厂仓
                        updateRequest.setFactoryCode(mn.getFactoryCode());
                        if (mn.getFactoryCode() == null) {
                            continue;
                        }
                        // 货品
                        updateRequest.setGoodsCode(mn.getScItemId());
                        if (mn.getScItemId() == null) {
                            continue;
                        }
                        // 生产日期
                        updateRequest.setPlanDate(mn.getProductionTime());
                        if (mn.getProductionTime() == null) {
                            continue;
                        }
                        // 单位
                        updateRequest.setUnit(mn.getSaleUnit());
                        if (mn.getSaleUnit() == null) {
                            continue;
                        }
                        if (mn.getBaseUnitQuantity() == null) {
                            continue;
                        }
                        updateRequest.setQuantity(Integer.valueOf(mn.getBaseUnitQuantity()));
                        log.info("externalSyncMaterial_updatePlanOrder request:{}", JSON.toJSON(updateRequest));
                        Result<Boolean> booleanResult = mnInventoryCenterService.sapUpdatePlanOrderQuantity(updateRequest);
                        log.info("externalSyncMaterial_updatePlanOrder response:{}", JSON.toJSON(booleanResult));
                    }
                }
            }
        } catch (Throwable e) {
            log.error("MnSapIf004ServiceImpl_externalSyncMaterial_updatePlanOrder_error", e);
        }
    }

    private Date strToDate(String str) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            return sdf.parse(str);
        } catch (ParseException e) {
            log.error("str to date error updatePlanOrder");
            log.error("MnSapIf004ServiceImpl_externalSyncMaterial_updatePlanOrder_error", e);
        }
        return null;
    }

    /**
     * 判断物料凭证是否在试点范围内的
     * @param sdo
     * @return
     */
    public boolean isInPilotScope(MnSapIf004SDO sdo){
        List<MnSapIf004Detail> detailList = Optional.ofNullable(sdo).map(MnSapIf004SDO::getItems).orElse(Lists.newArrayList());
        return Nullable.stream(detailList).findFirst()
                .map(detail -> warehousePilotService.isInPilotScope(detail.getFactoryCode(),detail.getInventoryCode()))
                .orElse(Boolean.FALSE);
    }

    /**
     * 根据工厂编码 + 库存地点
     * @param factoryCode
     * @param warehouseCode
     * @return
     */
    public boolean isInPilotScope(String factoryCode,String warehouseCode){
//        com.alibaba.cloud.retail.link.sdk.dto.Result<Boolean> result = pilotLogicWarehouseService.checkPilotByFactoryCodeAndInventoryCode(factoryCode, warehouseCode);
        CheckPilotByFactoryCodeAndInventoryCodeRequest request = new CheckPilotByFactoryCodeAndInventoryCodeRequest();
        if (StringUtils.isNotBlank(factoryCode)){
            request.setFactoryCode(factoryCode);
        }
        if (StringUtils.isNotBlank(warehouseCode)){
            request.setInventoryCode(warehouseCode);
        }
        Result<Boolean> result = otsApplicationAdapterSdkService.checkPilotByFactoryCodeAndInventoryCode(request);
        return Optional.ofNullable(result).map(Result::getResult).orElse(Boolean.FALSE);
    }

    public boolean isInPilotScope(String logicWarehouseCode){
        CheckPilotByLogicWarehouseCodeRequest request = new CheckPilotByLogicWarehouseCodeRequest();
        if (StringUtils.isNotBlank(logicWarehouseCode)){
            request.setLogicWarehouseCode(logicWarehouseCode);
        }
        Result<Boolean> result = otsApplicationAdapterSdkService.checkPilotByLogicWarehouseCode(request);
//        com.alibaba.cloud.retail.link.sdk.dto.Result<Boolean> result = pilotLogicWarehouseService.checkPilotByLogicWarehouseCode(logicWarehouseCode);
        return Optional.ofNullable(result).map(Result::getResult).orElse(Boolean.FALSE);
    }
    public String getBizSubOrderId(int isAllocate,MnSapIf004Detail mnSapIf004Detail,
                                   List<AllocateOrderDetail> allocateOrderDetailList,
                                   List<OrderLine> orderLineList){
        if (isAllocate == 1){
            return Nullable.stream(allocateOrderDetailList).filter(detail -> Objects.equals(deleteZeroPrefix(mnSapIf004Detail.getOutAllocateOrderLineId()),detail.getOutAllocateOrderLineId())
                || Objects.equals(deleteZeroPrefix(mnSapIf004Detail.getOutSourceOrderLineId()),detail.getOutAllocateOrderLineId()))
                    .findFirst().map(AllocateOrderDetail::getLineNo).map(String::valueOf).orElse(null);
        }else if (isAllocate == 2){
            return Nullable.stream(orderLineList).filter(orderLine -> Objects.equals(deleteZeroPrefix(mnSapIf004Detail.getOutSourceOrderLineId()),orderLine.getOuterOrderLineId()))
                    .findFirst().map(OrderLine::getSourceLineNum).orElse(null);
        }
        return null;
    }

    public static String deleteZeroPrefix(String str){
        if (StringUtils.isBlank(str)){
            return null;
        }
        return str.replaceFirst("^0*","");
    }

    private void saveShippingOrder(String inOutBoundOrder, MnSapIf004Request mnCrmIf004Request) {
        try {
            String transportNo = mnCrmIf004Request.getData().get(0).getExt();
            if (StringUtils.isBlank(transportNo)) {
                log.info("saveShippingOrderBy004 skip sapTransportNo is empty. 004Key={}", mnCrmIf004Request.getData().get(0).getHeaderKey());
                return;
            }
            MnOtwCommonResponse mnOtwCommonResponse = new MnOtwCommonResponse();
            JSONObject message = JSON.parseObject(JSON.toJSONString(mnCrmIf004Request));
            message.put("inOutBoundOrderNo", inOutBoundOrder);
            message.put("transportNo", transportNo);
            JSONObject request = new JSONObject();
            request.put("message", message.toJSONString());
            Map<String, String> header = HttpTokenUtil.generateHttpHeader();
            String urlPrefix = OtsApplicationAdapterSdkConstants.urlPrefix();
            String url = urlPrefix + "api/mn_bc_link/saveShippingOrderBy004";
            log.info("saveShippingOrderBy004 http header:{}, url:{}, param:{}", header, url, request.toJSONString());
            ResponseEntity<com.alibaba.cirtus.ots.application.adapter.ability.model.Result> post = httpUtils.post(url, header, request, com.alibaba.cirtus.ots.application.adapter.ability.model.Result.class);
            com.alibaba.cirtus.ots.application.adapter.ability.model.Result result = post.getBody();
            log.info("saveShippingOrderBy004 result:{}", JSON.toJSONString(result));
            if (result.isSuccess()) {
                mnOtwCommonResponse.setSuccess(OtwConstant.SUCCESS_TRUE);
                mnOtwCommonResponse.setResult(result.getResult());
            } else {
                mnOtwCommonResponse.setSuccess(OtwConstant.SUCCESS_FALSE);
                mnOtwCommonResponse.setErrorCode(result.getErrorCode());
                mnOtwCommonResponse.setMessage(result.getMessage());
            }
        } catch (Exception e) {
            log.error("saveShippingOrderBy004 error", e);
        }
    }

    //private void  shippingOrderDetailUpdateInOutBoundOrder(String inOutBoundOrder,String deliveryOrderNo){
    //    MnOtwCommonResponse mnOtwCommonResponse = new MnOtwCommonResponse();
    //    ShippingOrderDetailUpdateInOutBoundOrderReq shippingOrderDetailUpdateInOutBoundOrderReq = new ShippingOrderDetailUpdateInOutBoundOrderReq();
    //    List<String> deliveryOrderNoList = Lists.newArrayList(deliveryOrderNo);
    //    shippingOrderDetailUpdateInOutBoundOrderReq.setInOutBoundOrder(inOutBoundOrder);
    //    shippingOrderDetailUpdateInOutBoundOrderReq.setDeliveryOrderNoList(deliveryOrderNoList);
    //    Map<String, String> header = HttpTokenUtil.generateHttpHeader();
    //    String urlPrefix = OtsApplicationAdapterSdkConstants.urlPrefix();
    //    String url = urlPrefix + "api/mn_bc_link/shippingOrderDetailUpdateInOutBoundOrder";
    //    log.info("shippingOrderDetailUpdateInOutBoundOrder http header:{}, url:{}, param:{}", header, url, JSON.toJSONString(shippingOrderDetailUpdateInOutBoundOrderReq));
    //    ResponseEntity<com.alibaba.cirtus.ots.application.adapter.ability.model.Result> post = httpUtils.post(url, header, shippingOrderDetailUpdateInOutBoundOrderReq, com.alibaba.cirtus.ots.application.adapter.ability.model.Result.class);
    //    com.alibaba.cirtus.ots.application.adapter.ability.model.Result result = post.getBody();
    //    log.info("shippingOrderDetailUpdateInOutBoundOrder   result:{}", JSON.toJSONString(result));
    //    if (result.isSuccess()) {
    //        mnOtwCommonResponse.setSuccess(OtwConstant.SUCCESS_TRUE);
    //        mnOtwCommonResponse.setResult(result.getResult());
    //    } else {
    //        mnOtwCommonResponse.setSuccess(OtwConstant.SUCCESS_FALSE);
    //        mnOtwCommonResponse.setErrorCode(result.getErrorCode());
    //        mnOtwCommonResponse.setMessage(result.getMessage());
    //    }
    //}
}
